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

# Python SDK reference

> Every public class, method, and type in lighton-sdk 1.0.0.

<Info>
  Generated from [`lighton-sdk`](https://github.com/lightonai/lighton-python-sdk) **1.0.0**.
  Install it and see runnable examples in the [quick start](/sdks/python/quickstart).
</Info>

## Client

### LightOn

#### `LightOn.ask()`

```python theme={null}
ask(
    query: str,
    *,
    workspaces: list[Workspace | int] | None = None,
    tags: list[Tag | int | str] | None = None,
    files: list[File | int] | None = None,
    max_results: int | None = None,
    relevance_scoring: RelevanceScoring | None = None,
    model: str | None = None,
) -> AskResponse
```

POST /api/v3/ask, ask a grounded question over indexed documents.

**Arguments**

| Name                | Type                              | Description                                                                                                                                                  |
| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `query`             | `str`                             | Natural-language question (max 1500 chars).                                                                                                                  |
| `workspaces`        | `list[Workspace \| int] \| None`  | Restrict to these workspaces (Workspace objects or ids). Excludes files.                                                                                     |
| `tags`              | `list[Tag \| int \| str] \| None` | Restrict to documents carrying any of these tags, Tag objects, ids, or names (OR-matched). Names are resolved via Tag.list() and must exist. Excludes files. |
| `files`             | `list[File \| int] \| None`       | Restrict to these files (File objects or ids). Excludes workspaces and tags.                                                                                 |
| `max_results`       | `int \| None`                     | Chunks to retrieve for context (1–50; server default 10).                                                                                                    |
| `relevance_scoring` | `RelevanceScoring \| None`        | RelevanceScoring, .scoring\_and\_filtering (default), .scoring\_only, or .none.                                                                              |
| `model`             | `str \| None`                     | LLM for answer generation; platform default if omitted.                                                                                                      |

**Returns**

| Type          | Description                                         |
| ------------- | --------------------------------------------------- |
| `AskResponse` | The answer plus the ranked results used as context. |

#### `LightOn.close()`

```python theme={null}
close() -> None
```

#### `LightOn.extract()`

```python theme={null}
extract(
    schema: type[BaseModel] | dict[str, Any],
    *,
    path: str | Path | None = None,
    url: str | None = None,
    options: dict[str, Any] | None = None,
    mode: ExecMode = ExecMode.SYNC,
) -> ExtractJobResponse | ExtractJob
```

POST /api/v3/extract, extract structured data from a document.

Pass exactly one of:
path: A local file to upload (multipart).
url: A publicly accessible URL to fetch.

**Arguments**

| Name      | Type                                | Description                                                                                                                                                                       |
| --------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema`  | `type[BaseModel] \| dict[str, Any]` | The guided-generation schema driving extraction, either a pydantic model class (converted to a vLLM `response_format` JSON Schema) or a dict already holding a valid such schema. |
| `options` | `dict[str, Any] \| None`            | Free-form request options; currently `{"async": bool}`.                                                                                                                           |
| `mode`    | `ExecMode`                          | ExecMode.SYNC (default) runs inline and returns the extracted data. ExecMode.ASYNC queues the job and returns an `ExtractJob` handle, call `.poll()` until `.succeeded`.          |

**Returns**

| Type                               | Description                                                                     |
| ---------------------------------- | ------------------------------------------------------------------------------- |
| `ExtractJobResponse \| ExtractJob` | `ExtractJobResponse` (with data) when sync; a pollable `ExtractJob` when async. |

**Raises**

| Exception    | When                                              |
| ------------ | ------------------------------------------------- |
| `ValueError` | extract() requires exactly one of 'path' or 'url' |

#### `LightOn.parse()`

```python theme={null}
parse(
    *,
    path: str | Path | None = None,
    url: str | None = None,
    mode: ExecMode = ExecMode.SYNC,
) -> ParseResponse | ParseJob
```

POST /api/v3/parse, parse a document into per-page text.

Pass exactly one of:
path: A local file to upload (multipart).
url: A publicly accessible URL to fetch.

**Arguments**

| Name   | Type       | Description                                                                                                                                                                 |
| ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode` | `ExecMode` | ExecMode.SYNC (default) runs inline and returns the full `ParseResponse`. ExecMode.ASYNC queues the job and returns a `ParseJob` handle, call `.poll()` until `.succeeded`. |

**Returns**

| Type                        | Description                                                  |
| --------------------------- | ------------------------------------------------------------ |
| `ParseResponse \| ParseJob` | `ParseResponse` when sync; a pollable `ParseJob` when async. |

**Raises**

| Exception    | When                                            |
| ------------ | ----------------------------------------------- |
| `ValueError` | parse() requires exactly one of 'path' or 'url' |

#### `LightOn.search()`

```python theme={null}
search(
    query: str,
    *,
    workspaces: list[Workspace | int] | None = None,
    tags: list[Tag | int | str] | None = None,
    files: list[File | int] | None = None,
    max_results: int | None = None,
    mode: SearchMode | None = None,
    relevance_scoring: RelevanceScoring | None = None,
    include_image: bool | None = None,
    include_bboxes: bool | None = None,
) -> SearchResponse
```

POST /api/v3/search, retrieve relevant passages (no generation).

**Arguments**

| Name                | Type                              | Description                                                                                                                                                  |
| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `query`             | `str`                             | Natural-language search query (max 4000 chars).                                                                                                              |
| `workspaces`        | `list[Workspace \| int] \| None`  | Restrict to these workspaces (Workspace objects or ids). Excludes files.                                                                                     |
| `tags`              | `list[Tag \| int \| str] \| None` | Restrict to documents carrying any of these tags, Tag objects, ids, or names (OR-matched). Names are resolved via Tag.list() and must exist. Excludes files. |
| `files`             | `list[File \| int] \| None`       | Restrict to these files (File objects or ids). Excludes workspaces and tags.                                                                                 |
| `max_results`       | `int \| None`                     | Chunks to return after reranking (1–100; server default 10).                                                                                                 |
| `mode`              | `SearchMode \| None`              | SearchMode.text (hybrid keyword+vector) or .vision (page-image).                                                                                             |
| `relevance_scoring` | `RelevanceScoring \| None`        | RelevanceScoring, .scoring\_and\_filtering (default), .scoring\_only, or .none.                                                                              |
| `include_image`     | `bool \| None`                    | Attach a base64 page image to each result.                                                                                                                   |
| `include_bboxes`    | `bool \| None`                    | Attach chunk bounding boxes (PDF text-mode only).                                                                                                            |

**Returns**

| Type             | Description                |
| ---------------- | -------------------------- |
| `SearchResponse` | The ranked search results. |

### LightOnConfiguration

Non-essential client knobs. api\_key stays a direct LightOn() argument.

| Field                     | Type                          | Default                                                     | Description                                                                                                                                                                                                        |
| ------------------------- | ----------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `base_url`                | `str`                         | `'https://api.lighton.ai'`                                  |                                                                                                                                                                                                                    |
| `timeout`                 | `Timeout`                     | `Timeout(connect=5.0, read=120.0, write=120.0, pool=120.0)` |                                                                                                                                                                                                                    |
| `retries`                 | `int`                         | `3`                                                         | Connection-level retries (httpx transport).                                                                                                                                                                        |
| `transport`               | `httpx.BaseTransport \| None` | `None`                                                      |                                                                                                                                                                                                                    |
| `max_requests_per_minute` | `int \| None`                 | `1000`                                                      | Pace ALL requests to stay under this per-minute cap (min-interval gate in \_request). Defaults to 1000, the API's limit for most endpoints; override if your account differs. Set None to disable pacing entirely. |
| `rate_limit_retries`      | `int`                         | `3`                                                         | On HTTP 429, retry this many times, waiting the Retry-After header when present (else exponential backoff). 0 disables.                                                                                            |

## Workspaces & files

### Workspace

| Field                    | Type                        | Default    | Description                                               |
| ------------------------ | --------------------------- | ---------- | --------------------------------------------------------- |
| `id`                     | `int \| None`               | `None`     | Server-assigned id; None until created/retrieved.         |
| `name`                   | `str`                       | *required* | Workspace display name.                                   |
| `description`            | `str`                       | `''`       | Free-text workspace description.                          |
| `workspace_type`         | `str \| None`               | `None`     | Workspace type (read-only).                               |
| `document_upload_method` | `str \| None`               | `None`     | How documents are uploaded to this workspace (read-only). |
| `files_count`            | `int \| None`               | `None`     | Number of files in the workspace (read-only).             |
| `used_storage`           | `float \| None`             | `None`     | Bytes of storage used (read-only).                        |
| `created_at`             | `datetime.datetime \| None` | `None`     | Creation timestamp (read-only).                           |
| `updated_at`             | `datetime.datetime \| None` | `None`     | Last-update timestamp (read-only).                        |

#### `Workspace.create()`

```python theme={null}
create(client: LightOn) -> Workspace
```

Create this workspace and bind the client for later lifecycle calls.

**Arguments**

| Name     | Type      | Description                                                 |
| -------- | --------- | ----------------------------------------------------------- |
| `client` | `LightOn` | The client to create the workspace with and bind to `self`. |

**Returns**

| Type        | Description                                                       |
| ----------- | ----------------------------------------------------------------- |
| `Workspace` | `self`, updated with the server-assigned id and read-only fields. |

#### `Workspace.delete()`

```python theme={null}
delete() -> None
```

Delete this resource and clear its local id.

#### `Workspace.get()`

*Classmethod.*

```python theme={null}
get(client: LightOn, id: int | str) -> Self
```

Fetch a single resource by id.

**Arguments**

| Name     | Type         | Description                                                 |
| -------- | ------------ | ----------------------------------------------------------- |
| `client` | `LightOn`    | The client used to make the request and bind to the result. |
| `id`     | `int \| str` | The resource id to retrieve.                                |

**Returns**

| Type   | Description                      |
| ------ | -------------------------------- |
| `Self` | The resource, bound to `client`. |

#### `Workspace.ingest()`

```python theme={null}
ingest(
    file: File,
    *,
    wait: bool = False,
    timeout: float = 300.0,
    tags: list[int] | None = None,
) -> File
```

Upload a File into this workspace, uploading is the ingestion.

Non-blocking by default: the returned File is 'pending', poll it via
refresh()/wait(). Pass wait=True to block until ingestion is terminal.

**Arguments**

| Name      | Type                | Description                                                     |
| --------- | ------------------- | --------------------------------------------------------------- |
| `file`    | `File`              | The File to upload; its workspace\_id is set to this workspace. |
| `wait`    | `bool`              | If True, block until ingestion reaches a terminal status.       |
| `timeout` | `float`             | Seconds to wait when wait=True before raising TimeoutError.     |
| `tags`    | `list[int] \| None` | Optional tag ids to assign to the document on upload.           |

**Returns**

| Type   | Description                                         |
| ------ | --------------------------------------------------- |
| `File` | The created File, bound to this workspace's client. |

**Raises**

| Exception    | When                                                  |
| ------------ | ----------------------------------------------------- |
| `ValueError` | If this workspace has not been created/retrieved yet. |

#### `Workspace.ingest_many()`

```python theme={null}
ingest_many(
    files: list[File | str | Path],
    *,
    mode: ExecMode = ExecMode.SYNC,
    ignore_errors: bool = False,
    wait: bool = False,
    timeout: float = 300.0,
    max_workers: int = 8,
    tags: list[int] | None = None,
) -> BatchIngest | BatchIngestJob
```

Upload many files into this workspace, concurrently.

Every local path is validated to exist **before** any upload starts (fail
fast). Staying under the API rate limit and the 429 cooldown are handled by
the client (see LightOnConfiguration.max\_requests\_per\_minute /
rate\_limit\_retries), so they apply across uploads and status polls alike.

**Arguments**

| Name            | Type                        | Description                                                                                                                                                                                |
| --------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `files`         | `list[File \| str \| Path]` | Items to ingest, File objects, path strings, or Paths (mixed). A string with glob characters (`*?[`) is expanded (recursive `**` supported) to its matching files; duplicates are ignored. |
| `mode`          | `ExecMode`                  | ExecMode.SYNC blocks and returns a BatchIngest; ExecMode.ASYNC returns a BatchIngestJob you poll for progress/failures.                                                                    |
| `ignore_errors` | `bool`                      | If False (default), the first failure raises (sync) or surfaces on job.wait() (async). If True, failures are collected and the batch continues.                                            |
| `wait`          | `bool`                      | If True, wait for each upload's ingestion to reach a terminal status (embedded); if False, return once uploads are accepted.                                                               |
| `timeout`       | `float`                     | Per-file seconds to wait for ingestion when wait=True.                                                                                                                                     |
| `max_workers`   | `int`                       | Concurrent upload/poll threads.                                                                                                                                                            |
| `tags`          | `list[int] \| None`         | Optional tag ids assigned to every uploaded document.                                                                                                                                      |

**Returns**

| Type                            | Description                                       |
| ------------------------------- | ------------------------------------------------- |
| `BatchIngest \| BatchIngestJob` | A BatchIngest (SYNC) or a BatchIngestJob (ASYNC). |

**Raises**

| Exception           | When                                                            |
| ------------------- | --------------------------------------------------------------- |
| `ValueError`        | If this workspace has no id, or an item is a File with no path. |
| `FileNotFoundError` | If any path is missing and ignore\_errors is False.             |

#### `Workspace.list()`

*Classmethod.*

```python theme={null}
list(client: LightOn, **params: object) -> list[Self]
```

List every resource, following pagination to the end.

**Arguments**

| Name       | Type      | Description                                                         |
| ---------- | --------- | ------------------------------------------------------------------- |
| `client`   | `LightOn` | The client used to make the request and bind to each result.        |
| `**params` | `object`  | Optional query filters (e.g. workspace\_id) sent on the first page. |

**Returns**

| Type         | Description                                     |
| ------------ | ----------------------------------------------- |
| `list[Self]` | All matching resources, each bound to `client`. |

#### `Workspace.refresh()`

```python theme={null}
refresh() -> Self
```

Re-fetch this resource from the API (GET).

**Returns**

| Type   | Description                                   |
| ------ | --------------------------------------------- |
| `Self` | `self`, updated with the latest field values. |

#### `Workspace.save()`

```python theme={null}
save() -> Workspace
```

Persist local edits to name/description (PATCH).

**Returns**

| Type        | Description                                   |
| ----------- | --------------------------------------------- |
| `Workspace` | `self`, refreshed with the server's response. |

### File

| Field           | Type                          | Default | Description                                                                  |
| --------------- | ----------------------------- | ------- | ---------------------------------------------------------------------------- |
| `id`            | `int \| None`                 | `None`  | Server-assigned id; None until created/retrieved.                            |
| `path`          | `pathlib._local.Path \| None` | `None`  | Local source file to upload; set before create(), never in a response.       |
| `workspace_id`  | `int \| None`                 | `None`  | Target workspace for upload; required to create (Workspace.ingest fills it). |
| `filename`      | `str \| None`                 | `None`  | Document filename; defaults to path.name on upload.                          |
| `title`         | `str \| None`                 | `None`  | Document title; defaults to the filename server-side.                        |
| `status`        | `enums.FileStatus \| None`    | `None`  | Ingestion pipeline status (read-only).                                       |
| `status_detail` | `str \| None`                 | `None`  | Free-text error detail, present only on failure (read-only).                 |
| `extension`     | `str \| None`                 | `None`  | File extension of the document (read-only).                                  |
| `total_pages`   | `int \| None`                 | `None`  | Total page count of the document (read-only).                                |
| `size`          | `int \| None`                 | `None`  | File size in bytes (read-only).                                              |
| `created_at`    | `datetime.datetime \| None`   | `None`  | Creation timestamp (read-only).                                              |
| `updated_at`    | `datetime.datetime \| None`   | `None`  | Last-update timestamp (read-only).                                           |

#### `File.classify()`

```python theme={null}
classify(content_type: ContentType | str) -> File
```

Assign a content type to this file (ContentType object or path string).

**Arguments**

| Name           | Type                 | Description                                            |
| -------------- | -------------------- | ------------------------------------------------------ |
| `content_type` | `ContentType \| str` | The content type to assign, e.g. "legal:contract:nda". |

**Returns**

| Type   | Description |
| ------ | ----------- |
| `File` | `self`.     |

**Raises**

| Exception    | When                                             |
| ------------ | ------------------------------------------------ |
| `ValueError` | If this file has not been created/retrieved yet. |

#### `File.clear_attribute()`

```python theme={null}
clear_attribute(content_type: ContentType | str, name: str) -> File
```

Clear an attribute value under an assigned content type.

**Arguments**

| Name           | Type                 | Description                                        |
| -------------- | -------------------- | -------------------------------------------------- |
| `content_type` | `ContentType \| str` | The assigned content type (object or path string). |
| `name`         | `str`                | Attribute identifier to clear.                     |

**Returns**

| Type   | Description |
| ------ | ----------- |
| `File` | `self`.     |

#### `File.create()`

```python theme={null}
create(client: LightOn, *, tags: list[int] | None = None) -> File
```

Upload the file (multipart), this starts ingestion.

**Arguments**

| Name     | Type                | Description                                           |
| -------- | ------------------- | ----------------------------------------------------- |
| `client` | `LightOn`           | The client to upload with and bind to `self`.         |
| `tags`   | `list[int] \| None` | Optional tag ids to assign to the document on upload. |

**Returns**

| Type   | Description                                                     |
| ------ | --------------------------------------------------------------- |
| `File` | `self`, updated with the server-assigned id and initial status. |

**Raises**

| Exception    | When                                    |
| ------------ | --------------------------------------- |
| `ValueError` | If `path` or `workspace_id` is not set. |

#### `File.delete()`

```python theme={null}
delete() -> None
```

Delete this resource and clear its local id.

#### `File.facets()`

```python theme={null}
facets() -> list[Facet]
```

List this file's assigned content types and their attribute values.

**Returns**

| Type          | Description                                                      |
| ------------- | ---------------------------------------------------------------- |
| `list[Facet]` | One Facet per assigned content type (with its attribute values). |

**Raises**

| Exception    | When                                             |
| ------------ | ------------------------------------------------ |
| `ValueError` | If this file has not been created/retrieved yet. |

#### `File.get()`

*Classmethod.*

```python theme={null}
get(client: LightOn, id: int | str) -> Self
```

Fetch a single resource by id.

**Arguments**

| Name     | Type         | Description                                                 |
| -------- | ------------ | ----------------------------------------------------------- |
| `client` | `LightOn`    | The client used to make the request and bind to the result. |
| `id`     | `int \| str` | The resource id to retrieve.                                |

**Returns**

| Type   | Description                      |
| ------ | -------------------------------- |
| `Self` | The resource, bound to `client`. |

#### `File.get_by_name()`

*Classmethod.*

```python theme={null}
get_by_name(client: LightOn, name: str, *, workspace: Workspace | int) -> list[File]
```

Fetch every file with this user-facing name in a workspace.

Matches `title`, not `filename`: the server uniquifies filenames on upload
("report.pdf" is stored as something like "report\_20260728\_c9be.pdf"), so the
name you uploaded never matches the stored one. A title defaults to the
uploaded filename without its extension, so "report.pdf" and "report" both
find that upload.

Titles are not unique the way stored filenames are, uploading the same
document twice gives both copies the same title, so this returns every match
rather than picking one. Check the length if you need exactly one.

The API's `title` filter is a case-insensitive *partial* match, so the
candidates it returns are narrowed to an exact title match here.

**Arguments**

| Name        | Type               | Description                                                         |
| ----------- | ------------------ | ------------------------------------------------------------------- |
| `client`    | `LightOn`          | The client to query with and bind to the results.                   |
| `name`      | `str`              | The file's title, with or without an extension (e.g. "report.pdf"). |
| `workspace` | `Workspace \| int` | The workspace to search in (Workspace object or id).                |

**Returns**

| Type         | Description                                                              |
| ------------ | ------------------------------------------------------------------------ |
| `list[File]` | Every File with that title, each bound to `client`; empty if none match. |

**Raises**

| Exception    | When                                                         |
| ------------ | ------------------------------------------------------------ |
| `ValueError` | If the workspace has not been created/retrieved (has no id). |

#### `File.list()`

*Classmethod.*

```python theme={null}
list(client: LightOn, **params: object) -> list[Self]
```

List every resource, following pagination to the end.

**Arguments**

| Name       | Type      | Description                                                         |
| ---------- | --------- | ------------------------------------------------------------------- |
| `client`   | `LightOn` | The client used to make the request and bind to each result.        |
| `**params` | `object`  | Optional query filters (e.g. workspace\_id) sent on the first page. |

**Returns**

| Type         | Description                                     |
| ------------ | ----------------------------------------------- |
| `list[Self]` | All matching resources, each bound to `client`. |

#### `File.refresh()`

```python theme={null}
refresh() -> Self
```

Re-fetch this resource from the API (GET).

**Returns**

| Type   | Description                                   |
| ------ | --------------------------------------------- |
| `Self` | `self`, updated with the latest field values. |

#### `File.save()`

```python theme={null}
save() -> File
```

Persist local edits to title (PATCH). filename is immutable server-side.

**Returns**

| Type   | Description                                   |
| ------ | --------------------------------------------- |
| `File` | `self`, refreshed with the server's response. |

#### `File.set_attribute()`

```python theme={null}
set_attribute(content_type: ContentType | str, name: str, value: object) -> File
```

Set an attribute value under an assigned content type.

**Arguments**

| Name           | Type                 | Description                                                                                                               |
| -------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `content_type` | `ContentType \| str` | The assigned content type (object or path string).                                                                        |
| `name`         | `str`                | Attribute identifier (snake\_case).                                                                                       |
| `value`        | `object`             | The value; shape depends on the attribute type (string, number, date "YYYY-MM-DD", bool, or list\[str] for multi-select). |

**Returns**

| Type   | Description |
| ------ | ----------- |
| `File` | `self`.     |

#### `File.tag()`

```python theme={null}
tag(tags: list[Tag | int | str]) -> File
```

Assign tags to this file (POST /files/\<id>/tags).

**Arguments**

| Name   | Type                      | Description                                                                                                               |
| ------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `tags` | `list[Tag \| int \| str]` | Tags to add, Tag objects, ids, or names (mix freely). Names are resolved via Tag.list() and must exist. Empty is a no-op. |

**Returns**

| Type   | Description                          |
| ------ | ------------------------------------ |
| `File` | `self`, refreshed from the response. |

**Raises**

| Exception    | When                                                       |
| ------------ | ---------------------------------------------------------- |
| `ValueError` | If this file isn't persisted, or a name/Tag can't resolve. |

#### `File.unclassify()`

```python theme={null}
unclassify(content_type: ContentType | str) -> File
```

Remove a content-type assignment from this file.

**Arguments**

| Name           | Type                 | Description                                           |
| -------------- | -------------------- | ----------------------------------------------------- |
| `content_type` | `ContentType \| str` | The content type to unassign (object or path string). |

**Returns**

| Type   | Description |
| ------ | ----------- |
| `File` | `self`.     |

#### `File.untag()`

```python theme={null}
untag(tags: list[Tag | int | str]) -> File
```

Remove tags from this file (DELETE /files/\<id>/tags/\<tag\_id>, one each).

**Arguments**

| Name   | Type                      | Description                                                                                                                  |
| ------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `tags` | `list[Tag \| int \| str]` | Tags to remove, Tag objects, ids, or names (mix freely). Names are resolved via Tag.list() and must exist. Empty is a no-op. |

**Returns**

| Type   | Description |
| ------ | ----------- |
| `File` | `self`.     |

**Raises**

| Exception    | When                                                       |
| ------------ | ---------------------------------------------------------- |
| `ValueError` | If this file isn't persisted, or a name/Tag can't resolve. |

#### `File.wait()`

```python theme={null}
wait(timeout: float = 300.0, poll: float = 2.0) -> File
```

Block (polling) until ingestion reaches a terminal state.

ponytail: dumb poll loop, the API offers no webhook. Run in a thread
(or use wait\_all) for concurrency; the SDK stays sync.

**Arguments**

| Name      | Type    | Description                                      |
| --------- | ------- | ------------------------------------------------ |
| `timeout` | `float` | Max seconds to wait before raising TimeoutError. |
| `poll`    | `float` | Seconds to sleep between status checks.          |

**Returns**

| Type   | Description                                                          |
| ------ | -------------------------------------------------------------------- |
| `File` | `self`, once `status` is a terminal-success state (embedded/parsed). |

**Raises**

| Exception      | When                                           |
| -------------- | ---------------------------------------------- |
| `TimeoutError` | If `timeout` elapses before a terminal status. |
| `LightOnError` | If ingestion ends in a terminal-failure state. |

### wait\_all

```python theme={null}
wait_all(files: list[File], timeout: float = 300.0) -> list[File]
```

Wait for many ingestions concurrently (threads, not async, sync SDK).

**Arguments**

| Name      | Type         | Description                                                        |
| --------- | ------------ | ------------------------------------------------------------------ |
| `files`   | `list[File]` | The files to wait on; each is polled via File.wait.                |
| `timeout` | `float`      | Max seconds to wait per file before that wait raises TimeoutError. |

**Returns**

| Type         | Description                                                      |
| ------------ | ---------------------------------------------------------------- |
| `list[File]` | The same files, once each has reached a terminal-success status. |

**Raises**

| Exception      | When                                                      |
| -------------- | --------------------------------------------------------- |
| `TimeoutError` | If any file does not finish within `timeout`.             |
| `LightOnError` | If any file's ingestion ends in a terminal-failure state. |

## Tags & content types

### Tag

| Field            | Type                        | Default    | Description                                                  |
| ---------------- | --------------------------- | ---------- | ------------------------------------------------------------ |
| `id`             | `int \| None`               | `None`     | Server-assigned id; None until created/retrieved.            |
| `name`           | `str`                       | *required* | Tag name.                                                    |
| `description`    | `str`                       | `''`       | Free-text description (max 500 chars).                       |
| `auto_assign`    | `bool`                      | `True`     | If True the system may auto-assign this tag; else user-only. |
| `document_count` | `int \| None`               | `None`     | Number of documents carrying this tag (read-only).           |
| `created_at`     | `datetime.datetime \| None` | `None`     | Creation timestamp (read-only).                              |
| `updated_at`     | `datetime.datetime \| None` | `None`     | Last-update timestamp (read-only).                           |

#### `Tag.create()`

```python theme={null}
create(client: LightOn) -> Tag
```

Create this tag and bind the client for later lifecycle calls.

**Arguments**

| Name     | Type      | Description                                           |
| -------- | --------- | ----------------------------------------------------- |
| `client` | `LightOn` | The client to create the tag with and bind to `self`. |

**Returns**

| Type  | Description                                                       |
| ----- | ----------------------------------------------------------------- |
| `Tag` | `self`, updated with the server-assigned id and read-only fields. |

#### `Tag.delete()`

```python theme={null}
delete() -> None
```

Delete this resource and clear its local id.

#### `Tag.get()`

*Classmethod.*

```python theme={null}
get(client: LightOn, id: int | str) -> NoReturn
```

**Raises**

| Exception             | When                                                     |
| --------------------- | -------------------------------------------------------- |
| `NotImplementedError` | the tags API has no single-tag GET; use Tag.list(client) |

#### `Tag.list()`

*Classmethod.*

```python theme={null}
list(client: LightOn, **params: object) -> list[Self]
```

List every resource, following pagination to the end.

**Arguments**

| Name       | Type      | Description                                                         |
| ---------- | --------- | ------------------------------------------------------------------- |
| `client`   | `LightOn` | The client used to make the request and bind to each result.        |
| `**params` | `object`  | Optional query filters (e.g. workspace\_id) sent on the first page. |

**Returns**

| Type         | Description                                     |
| ------------ | ----------------------------------------------- |
| `list[Self]` | All matching resources, each bound to `client`. |

#### `Tag.refresh()`

```python theme={null}
refresh() -> NoReturn
```

**Raises**

| Exception             | When                                                     |
| --------------------- | -------------------------------------------------------- |
| `NotImplementedError` | the tags API has no single-tag GET; use Tag.list(client) |

### ContentType

A node in the content-type taxonomy.

| Field         | Type          | Default    | Description                                                    |
| ------------- | ------------- | ---------- | -------------------------------------------------------------- |
| `path`        | `str`         | *required* | Full taxonomy path, e.g. legal:contract:nda.                   |
| `code`        | `str`         | *required* | This node's own code segment.                                  |
| `label`       | `str`         | *required* | Human-readable label.                                          |
| `description` | `str`         | `''`       | Free-text description.                                         |
| `source`      | `str \| None` | `None`     | Where the type is defined (read-only).                         |
| `attributes`  | `list`        | `[]`       | Attribute definitions (present when include\_attributes=True). |
| `children`    | `list`        | `[]`       | Child content types.                                           |

#### `ContentType.list()`

*Classmethod.*

```python theme={null}
list(
    client: LightOn,
    *,
    path: str | None = None,
    depth: int | None = None,
    include_attributes: bool = False,
    query: str | None = None,
) -> list[ContentType]
```

List the content-type taxonomy (top-level nodes, each with `children`).

**Arguments**

| Name                 | Type          | Description                                    |
| -------------------- | ------------- | ---------------------------------------------- |
| `client`             | `LightOn`     | The client to query with.                      |
| `path`               | `str \| None` | Restrict to the subtree rooted at this path.   |
| `depth`              | `int \| None` | How many levels of children to return.         |
| `include_attributes` | `bool`        | Populate each node's `attributes` definitions. |
| `query`              | `str \| None` | Free-text filter over labels/paths.            |

**Returns**

| Type                | Description                       |
| ------------------- | --------------------------------- |
| `list[ContentType]` | The top-level content-type nodes. |

### Attribute

One attribute of a content type, a definition, or a value set on a file.

Carries both the schema (type/required/choices) and, when read from a file's
facets, the current `value`. `value` is None for bare definitions / when unset.

| Field         | Type   | Default    | Description                                             |
| ------------- | ------ | ---------- | ------------------------------------------------------- |
| `name`        | `str`  | *required* | Attribute identifier in snake\_case.                    |
| `label`       | `str`  | `''`       | Human-readable label.                                   |
| `type`        | `str`  | `''`       | text, number, date, boolean, select, or multi-select.   |
| `value`       | `Any`  | `None`     | Current value on the file; None for a definition/unset. |
| `required`    | `bool` | `False`    | Whether the schema requires it.                         |
| `choices`     | `list` | `[]`       | Allowed values for select/multi-select.                 |
| `description` | `str`  | `''`       | Optional attribute description.                         |

### Facet

A content type assigned to a file, with the file's attribute values on it.

| Field        | Type   | Default    | Description                             |
| ------------ | ------ | ---------- | --------------------------------------- |
| `path`       | `str`  | *required* | Assigned content-type path on the file. |
| `label`      | `str`  | *required* | Human-readable content-type label.      |
| `attributes` | `list` | `[]`       | Attribute values set on the file.       |

## API keys

### ApiKey

| Field        | Type                               | Default    | Description                                                                   |
| ------------ | ---------------------------------- | ---------- | ----------------------------------------------------------------------------- |
| `id`         | `str \| None`                      | `None`     | Server-assigned id; None until created/retrieved.                             |
| `name`       | `str`                              | *required* | API key display name.                                                         |
| `expires_at` | `datetime.datetime \| None`        | `None`     | Expiry timestamp; None for no expiry.                                         |
| `scopes`     | `list`                             | `[]`       | Per-workspace access scopes; empty for an unscoped key.                       |
| `prefix`     | `str \| None`                      | `None`     | Non-secret key prefix for identification (read-only).                         |
| `created_at` | `datetime.datetime \| None`        | `None`     | Creation timestamp (read-only).                                               |
| `key`        | `pydantic.types.SecretStr \| None` | `None`     | Plaintext secret, returned ONLY by create(), once. Use .get\_secret\_value(). |

#### `ApiKey.create()`

```python theme={null}
create(client: LightOn) -> ApiKey
```

Create this API key and bind the client for later lifecycle calls.

**Arguments**

| Name     | Type      | Description                                           |
| -------- | --------- | ----------------------------------------------------- |
| `client` | `LightOn` | The client to create the key with and bind to `self`. |

**Returns**

| Type     | Description                                                   |
| -------- | ------------------------------------------------------------- |
| `ApiKey` | `self`, updated with the id and the one-time plaintext `key`. |

#### `ApiKey.delete()`

```python theme={null}
delete() -> None
```

Delete this resource and clear its local id.

#### `ApiKey.get()`

*Classmethod.*

```python theme={null}
get(client: LightOn, id: int | str) -> Self
```

Fetch a single resource by id.

**Arguments**

| Name     | Type         | Description                                                 |
| -------- | ------------ | ----------------------------------------------------------- |
| `client` | `LightOn`    | The client used to make the request and bind to the result. |
| `id`     | `int \| str` | The resource id to retrieve.                                |

**Returns**

| Type   | Description                      |
| ------ | -------------------------------- |
| `Self` | The resource, bound to `client`. |

#### `ApiKey.list()`

*Classmethod.*

```python theme={null}
list(client: LightOn, **params: object) -> list[Self]
```

List every resource, following pagination to the end.

**Arguments**

| Name       | Type      | Description                                                         |
| ---------- | --------- | ------------------------------------------------------------------- |
| `client`   | `LightOn` | The client used to make the request and bind to each result.        |
| `**params` | `object`  | Optional query filters (e.g. workspace\_id) sent on the first page. |

**Returns**

| Type         | Description                                     |
| ------------ | ----------------------------------------------- |
| `list[Self]` | All matching resources, each bound to `client`. |

#### `ApiKey.refresh()`

```python theme={null}
refresh() -> Self
```

Re-fetch this resource from the API (GET).

**Returns**

| Type   | Description                                   |
| ------ | --------------------------------------------- |
| `Self` | `self`, updated with the latest field values. |

#### `ApiKey.save()`

```python theme={null}
save() -> ApiKey
```

Persist local edits to name/scopes (PATCH).

**Returns**

| Type     | Description                                                             |
| -------- | ----------------------------------------------------------------------- |
| `ApiKey` | `self`, refreshed with the server's response (never re-includes `key`). |

### ApiKeyScope

| Field          | Type   | Default    | Description                            |
| -------------- | ------ | ---------- | -------------------------------------- |
| `workspace_id` | `int`  | *required* | Workspace this scope grants access to. |
| `role`         | `Role` | *required* | Access role granted on the workspace.  |

## Jobs & batches

### ParseJob

| Field                | Type                                   | Default    | Description                                                 |
| -------------------- | -------------------------------------- | ---------- | ----------------------------------------------------------- |
| `id`                 | `str`                                  | *required* | Async job id; poll its endpoint for status.                 |
| `status`             | `str`                                  | *required* | Current status; compare against JobStatus (see .succeeded). |
| `created_at`         | `pydantic.types.AwareDatetime \| None` | `None`     | When the job was accepted.                                  |
| `completed_at`       | `pydantic.types.AwareDatetime \| None` | `None`     | Set once the job is terminal (success or failure).          |
| `processing_time_ms` | `int \| None`                          | `None`     | Wall-clock processing time, populated once terminal.        |
| `progress`           | `types.api.JobProgress \| None`        | `None`     | Live progress (percentage, pages) while in flight.          |
| `document`           | `types.api.ParseDocument \| None`      | `None`     | Parsed-document metadata, once completed.                   |
| `result`             | `types.api.ParseResult \| None`        | `None`     | Per-page text, populated once completed.                    |
| `usage`              | `types.api.ParseUsage \| None`         | `None`     | Pages processed, populated once completed.                  |
| `error`              | `types.api.ParseError \| None`         | `None`     | Failure detail on a terminal-failed job, else null.         |

#### `ParseJob.poll()`

```python theme={null}
poll(*, page: int | None = None) -> Self
```

Re-fetch this job's status from the API, updating this object in place.

**Arguments**

| Name   | Type          | Description                                                      |
| ------ | ------------- | ---------------------------------------------------------------- |
| `page` | `int \| None` | Page of `result.data` to fetch (extract only, results paginate). |

**Returns**

| Type   | Description                                      |
| ------ | ------------------------------------------------ |
| `Self` | `self`, refreshed with the latest status/result. |

**Raises**

| Exception    | When                                                                                 |
| ------------ | ------------------------------------------------------------------------------------ |
| `ValueError` | If the job isn't bound to a client (built by hand, not returned from parse/extract). |

### ExtractJob

| Field                | Type                                   | Default    | Description                                                 |
| -------------------- | -------------------------------------- | ---------- | ----------------------------------------------------------- |
| `id`                 | `str`                                  | *required* | Async job id; poll its endpoint for status.                 |
| `status`             | `str`                                  | *required* | Current status; compare against JobStatus (see .succeeded). |
| `created_at`         | `pydantic.types.AwareDatetime \| None` | `None`     | When the job was accepted.                                  |
| `completed_at`       | `pydantic.types.AwareDatetime \| None` | `None`     | Set once the job is terminal (success or failure).          |
| `processing_time_ms` | `int \| None`                          | `None`     | Wall-clock processing time, populated once terminal.        |
| `progress`           | `types.api.JobProgress \| None`        | `None`     | Live progress (percentage, pages) while in flight.          |
| `document`           | `types.api.ExtractDocument \| None`    | `None`     | Source-document metadata, once completed.                   |
| `result`             | `types.api.ExtractResult \| None`      | `None`     | Extracted data, populated once completed.                   |
| `usage`              | `types.api.ExtractUsage \| None`       | `None`     | Pages processed, populated once completed.                  |

#### `ExtractJob.poll()`

```python theme={null}
poll(*, page: int | None = None) -> Self
```

Re-fetch this job's status from the API, updating this object in place.

**Arguments**

| Name   | Type          | Description                                                      |
| ------ | ------------- | ---------------------------------------------------------------- |
| `page` | `int \| None` | Page of `result.data` to fetch (extract only, results paginate). |

**Returns**

| Type   | Description                                      |
| ------ | ------------------------------------------------ |
| `Self` | `self`, refreshed with the latest status/result. |

**Raises**

| Exception    | When                                                                                 |
| ------------ | ------------------------------------------------------------------------------------ |
| `ValueError` | If the job isn't bound to a client (built by hand, not returned from parse/extract). |

### BatchIngestJob

A running (or finished) batch ingestion.

Returned by `Workspace.ingest_many(mode=ExecMode.ASYNC)`. Uploads (and, when
`wait=True`, ingestion polls) run in a background thread; read `progress`,
`succeeded`, and `failed` at any time, or block with `wait()`.

#### `BatchIngestJob.poll()`

```python theme={null}
poll() -> BatchProgress
```

Return the current progress. Mirror of the parse/extract job's poll();
state updates itself in the background thread, so this just snapshots it.

#### `BatchIngestJob.wait()`

```python theme={null}
wait(timeout: float | None = None) -> BatchIngest
```

Block until the batch finishes, then return its result.

**Arguments**

| Name      | Type            | Description                                                  |
| --------- | --------------- | ------------------------------------------------------------ |
| `timeout` | `float \| None` | Max seconds to wait for the whole batch; None waits forever. |

**Returns**

| Type          | Description                 |
| ------------- | --------------------------- |
| `BatchIngest` | The terminal `BatchIngest`. |

**Raises**

| Exception      | When                                                                                        |
| -------------- | ------------------------------------------------------------------------------------------- |
| `TimeoutError` | If the batch doesn't finish within `timeout`.                                               |
| `Exception`    | The first upload/ingestion error, re-raised, when the batch ran with `ignore_errors=False`. |
| `_error`       |                                                                                             |

### BatchIngest

Terminal result of a batch: the Files that succeeded and what failed.

| Field       | Type   | Default | Description                    |
| ----------- | ------ | ------- | ------------------------------ |
| `succeeded` | `list` | `[]`    | Files that succeeded.          |
| `failed`    | `list` | `[]`    | Failures, each with its cause. |

### BatchProgress

Live snapshot of a running batch (see `BatchIngestJob.progress`).

| Field      | Type   | Default    | Description                                                             |
| ---------- | ------ | ---------- | ----------------------------------------------------------------------- |
| `total`    | `int`  | *required* | Total items requested.                                                  |
| `uploaded` | `int`  | *required* | Uploads accepted so far.                                                |
| `ingested` | `int`  | *required* | Files that reached a terminal-OK status (only advances when wait=True). |
| `failed`   | `int`  | *required* | Failures so far.                                                        |
| `done`     | `bool` | *required* | True once the batch is terminal.                                        |

### FailedIngest

One file that didn't make it.

| Field    | Type                | Default    | Description                                                                                                                     |
| -------- | ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `source` | `Path`              | *required* | The local path that failed.                                                                                                     |
| `error`  | `Exception`         | *required* | The exception that caused the failure.                                                                                          |
| `file`   | `file.File \| None` | `None`     | The uploaded File when the failure was at ingestion (not the upload itself); None if the upload failed or the path was missing. |

## Enums

### ExecMode

Execution mode for parse/extract: run inline or queue as an async job.

Uppercase members (unlike the other enums here) so the async member can be
`ASYNC`, lowercase `async` is a Python keyword and can't be a member name.

| Member  | Value   |
| ------- | ------- |
| `SYNC`  | `sync`  |
| `ASYNC` | `async` |

### FileStatus

Ingestion pipeline status for a File.

| Member               | Value                |
| -------------------- | -------------------- |
| `pending`            | `pending`            |
| `pending_conversion` | `pending_conversion` |
| `converting`         | `converting`         |
| `parsing`            | `parsing`            |
| `parsing_failed`     | `parsing_failed`     |
| `embedding`          | `embedding`          |
| `embedding_failed`   | `embedding_failed`   |
| `embedded`           | `embedded`           |
| `parsed`             | `parsed`             |
| `fail`               | `fail`               |
| `updating`           | `updating`           |

### JobStatus

Status of an async parse/extract job.

Only `pending` (initial) and `completed` (success) are documented by the
API, the schema types `status` as a bare string with no enum and doesn't
publish the failure vocabulary. This enum is for call-site comparisons
(StrEnum members equal their string values), NOT to validate the response
field, so an unrecognized server value compares unequal rather than erroring.
Detect terminal-failure via `completed_at` being set without `completed`
(or, for parse, the `error` block) rather than a status string.

| Member      | Value       |
| ----------- | ----------- |
| `pending`   | `pending`   |
| `completed` | `completed` |

### RelevanceScoring

Cross-encoder relevance scoring step for search.

| Member                  | Value                   |
| ----------------------- | ----------------------- |
| `none`                  | `none`                  |
| `scoring_only`          | `scoring_only`          |
| `scoring_and_filtering` | `scoring_and_filtering` |

### Role

Access role granted by an API-key scope on a workspace.

| Member   | Value    |
| -------- | -------- |
| `viewer` | `viewer` |
| `editor` | `editor` |
| `owner`  | `owner`  |

### SearchMode

Retrieval mode for search/ask.

| Member   | Value    |
| -------- | -------- |
| `text`   | `text`   |
| `vision` | `vision` |

## Exceptions

Importable from `lighton.exceptions`. Every method that performs a request can
raise these, so they are listed once here rather than repeated on each method.
A method's own **Raises** block covers only what it raises directly.

| Exception                | Inherits          | Raised when                                                                                                                                                                                                               |
| ------------------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `LightOnError`           | `Exception`       | Base class for every error raised by this SDK.                                                                                                                                                                            |
| `LightOnAPIError`        | `LightOnError`    | The API returned a non-2xx response.                                                                                                                                                                                      |
| `LightOnConnectionError` | `LightOnError`    | Transport failure before any response was received (DNS, timeout, reset).                                                                                                                                                 |
| `MalformedResponseError` | `LightOnError`    | A 2xx response body was not valid JSON.                                                                                                                                                                                   |
| `AuthenticationError`    | `LightOnAPIError` | 401, bad or missing API key (the request is not authenticated).                                                                                                                                                           |
| `NotFoundError`          | `LightOnAPIError` | 404, the resource does not exist.                                                                                                                                                                                         |
| `PermissionDeniedError`  | `LightOnAPIError` | 403, authenticated, but the key lacks permission for this operation. Distinct from `AuthenticationError`: the credentials are valid, but the caller isn't allowed (e.g. an endpoint that requires the CompanyAdmin role). |
| `RateLimitError`         | `LightOnAPIError` | 429, too many requests. `retry_after` is the seconds to wait before retrying, from the `Retry-After` response header when the server sends it (else None).                                                                |
| `ServerError`            | `LightOnAPIError` | 5xx, the API failed to handle the request.                                                                                                                                                                                |
