# Computed Files API

Source: https://userguide.qualytics.io/container/computed-files/api/

Programmatic access to Computed Files uses the generic **Container** endpoints on the Qualytics REST API. The requests below cover creation, update, deletion, validation, and the Timeline with its comments. Requests share the same base URL and Bearer token authentication as the rest of the platform API.

- **Base URL.** Your deployment's platform host (for example, `https://<your-tenant>.qualytics.io/api`).
- **Auth.** Bearer token in the `Authorization` header. See [Access Tokens](https://userguide.qualytics.io/settings/access-tokens/getting-started/) to create one.

> **Tip: Complete API Reference**
>
> The endpoints below are illustrative. For the full request and response schemas, live examples, and every field that each endpoint accepts, see the interactive API reference at [demo.qualytics.io/api/docs](https://demo.qualytics.io/api/docs).

> **Note: Permissions**
>
> - **Create, edit, delete**: requires the **Editor** team permission on the parent datastore, or the **Author** team permission when the user is the current owner (for Edit and Delete) or will be its owner (for Create, when `owner_id` is omitted or set to the caller's own user id).
> - **View, list, history**: requires the **Reporter** team permission or above.
> - **Validate**: requires the **Editor** team permission on the datastore, or **Author** when the caller will be the owner of the Computed File.
> - **Reassign owner**: requires the **Editor** team permission.
> - **Comments**: reading requires the **Reporter** team permission; posting and replying require the **Viewer** team permission. Only the author can edit their own comment, and deleting another user's comment requires the **Admin** role.

## Payload Shape

The Computed File is a specialization of the generic Container payload. Common fields:

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `container_type` | string | Yes | - | Discriminator identifying the container as a Computed File. Set to the literal `"computed_file"`. |
| `datastore_id` | integer | Yes (Create) | - | The DFS source datastore (S3, GCS, or Azure Data Lake Storage) the Computed File lives in. Passing a JDBC datastore returns `409 Conflict`. Set at creation; cannot be changed on Update. |
| `source_container_id` | integer | Yes (Create) | - | The base file container the Computed File is built on. Must be a file container in the same datastore, and must have been profiled at least once. Set at creation; cannot be changed on Update. |
| `name` | string | Yes | - | Max 255 characters. Spaces are replaced with underscores. Must be unique inside the datastore. |
| `select_clause` | string | Yes | - | The Spark SQL projection. The `DISTINCT` keyword is not allowed in any form (including `COUNT(DISTINCT ...)`, `APPROX_COUNT_DISTINCT(...)`, and `DISTINCT(col)`); use `GROUP BY` on the same columns, or `explode(collect_set(...))` for distinct-value semantics. |
| `where_clause` | string | No | `null` | Optional Spark SQL predicate. |
| `group_by_clause` | string | No | `null` | Optional. Required when `select_clause` uses aggregations. |
| `lateral_views` | array of strings | No | `null` | Optional list of Spark SQL lateral view bodies such as `EXPLODE(tags) t AS tag`. Qualytics prepends `LATERAL VIEW` automatically to each entry. |
| `description` | string | No | `null` | Max 8000 characters. |
| `owner_id` | integer | No | Caller's user id | Reassignment (on Update) requires the **Editor** team permission. |
| `additional_metadata` | object | No | `null` | Freeform key-value pairs. Keys also act as default values for [runtime variables](https://userguide.qualytics.io/operations/scan/how-tos/use-runtime-variables/) in this container's checks. |
| `force_drop_fields` | boolean | No (Update) | `false` | Set to `true` to save an update whose new clauses drop fields with attached quality checks or anomalies. |

## Create

**Endpoint:** `POST /containers`

**Permission:** **Editor** team permission on the datastore, or **Author** creating a Computed File they will own.

> **Example: Create with curl**
>
> ```bash
> curl -X POST "https://<your-tenant>.qualytics.io/api/containers" \
>   -H "Authorization: Bearer $QUALYTICS_TOKEN" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "container_type": "computed_file",
>     "datastore_id": 21,
>     "source_container_id": 108,
>     "name": "orders_placed_2026",
>     "select_clause": "customer_id, order_id, total",
>     "where_clause": "status = '\''placed'\'' AND year = 2026",
>     "description": "Orders in the placed status for the current year.",
>     "owner_id": 42
>   }'
> ```

Returns the created Container object with its own `id` (for example `512`), which differs from `source_container_id`. Use that `id` for the Update, Delete, and History calls below, so they act on the Computed File and not on the source file. Qualytics runs a basic profile that samples up to 1000 records per partition immediately after save, and a full asynchronous profile follows.

## Update

**Endpoint:** `PUT /containers/{id}`

**Permission:** **Editor** team permission on the datastore, or **Author** owner of the Computed File.

> **Example: Update with curl**
>
> ```bash
> curl -X PUT "https://<your-tenant>.qualytics.io/api/containers/512" \
>   -H "Authorization: Bearer $QUALYTICS_TOKEN" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "container_type": "computed_file",
>     "name": "orders_placed_2026",
>     "select_clause": "customer_id, order_id, total, region",
>     "where_clause": "status = '\''placed'\'' AND year = 2026",
>     "owner_id": 42
>   }'
> ```

`source_container_id` and `datastore_id` cannot be changed after creation. The Edit modal in the UI also hides the Name field, but the API accepts a new `name` on Update and will rename the container. Changing `owner_id` (reassigning ownership) requires the **Editor** team permission; Authors can edit Computed Files they own but cannot reassign them.

If the new clauses drop a field that has attached quality checks or anomalies, the update returns `409 Conflict` with a `dropped_fields` payload. Add `force_drop_fields: true` to the body to save anyway; the affected fields are marked as missing while their checks and anomalies are preserved.

When the update changes a clause (`select_clause`, `where_clause`, `group_by_clause`, or `lateral_views`), Qualytics re-profiles the Computed File immediately after save: a synchronous slim profile refreshes field statistics right away, and a full asynchronous profile follows. Metadata-only updates (Description, Owner, Additional Metadata) skip both profiles.

## Delete

**Endpoint:** `DELETE /containers/{id}`

**Permission:** **Editor** team permission on the datastore, or **Author** owner of the Computed File.

> **Example: Delete with curl**
>
> ```bash
> curl -X DELETE "https://<your-tenant>.qualytics.io/api/containers/512" \
>   -H "Authorization: Bearer $QUALYTICS_TOKEN"
> ```

Returns `204 No Content` on success. The container, its quality checks, anomalies, profile history, and scan history are removed. The delete is blocked with `409 Conflict` when quality checks in other containers reference this Computed File via `ref_container_id`. The rule types that can trigger the block are `existsIn`, `notExistsIn`, `dataDiff`, `aggregationComparison`, and `isReplicaOf` (sunsetting; use `dataDiff` for new checks). Delete or repoint those checks first. Archived checks normally do not block the delete, and an archived check that references this Computed File is permanently deleted together with the container. If an archived check is still listed in the `409` error, delete it first.

## Validate a Definition Before Save

**Endpoint:** `POST /containers/validate`

**Permission:** **Editor** team permission on the datastore, or **Author** validating a Computed File they will own.

Validates the Spark SQL against a zero-row view of the source without persisting anything. The request body carries the same payload you would send to Create or Update, wrapped in a `container` object:

> **Example: Validate with curl**
>
> ```bash
> curl -X POST "https://<your-tenant>.qualytics.io/api/containers/validate" \
>   -H "Authorization: Bearer $QUALYTICS_TOKEN" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "container": {
>       "container_type": "computed_file",
>       "datastore_id": 21,
>       "source_container_id": 108,
>       "name": "orders_placed_2026",
>       "select_clause": "customer_id, order_id, total",
>       "where_clause": "status = '\''placed'\''"
>     }
>   }'
> ```

Returns `204 No Content` on success. On failure, the response body carries a `detail` field with the underlying error message. The endpoint accepts a `timeout_seconds` query parameter (default 150, minimum 30, maximum 300); requests that exceed the window return `408 Request Timeout`.

## Timeline

**Endpoint:** `GET /containers/{id}/history`

**Permission:** **Reporter** team permission on the datastore or above.

> **Example: History endpoint with curl**
>
> ```bash
> curl -X GET "https://<your-tenant>.qualytics.io/api/containers/512/history" \
>   -H "Authorization: Bearer $QUALYTICS_TOKEN"
> ```

Returns the change log behind the container's **Timeline** section, including the Spark SQL clauses and metadata as they were at each revision. Use this to review previous versions of the definition or attribute changes to specific users.

## Timeline comments

**Endpoint:** `GET /containers/{id}/comments`

**Permission:** **Reporter** team permission on the datastore or above.

Returns the comments posted on the container's Timeline, newest first. Accepts `start_date` and `end_date` to bound comments that are not anchored to a change, and a repeatable `transaction_id` to fetch the comments anchored to specific Timeline entries.

Comments are created with `POST /comments` (**Viewer** team permission or above), updated with `PUT /comments/{id}` (author only), and deleted with `DELETE /comments/{id}` (author, or any user with the **Admin** role). See [Anomalies API](https://userguide.qualytics.io/anomalies/api/#list-an-anomalys-comments) for the payload fields, which are the same for every asset type.

## Read

**Endpoint:** `GET /containers/{id}` for a single container, `GET /containers?container_type=computed_file&datastore={id}` to list Computed Files inside a datastore.

**Permission:** **Reporter** team permission on the datastore or above.

## Error Responses

| Status | Meaning |
| --- | --- |
| `400 Bad Request` | The `DISTINCT` keyword is used in `select_clause`. Use `GROUP BY` on the same columns, or `explode(collect_set(...))` for distinct-value semantics. |
| `403 Forbidden` | Caller lacks the required team permission for the operation. |
| `404 Not Found` | Datastore, source file container, or Computed File does not exist. The response body identifies which one is missing. |
| `408 Request Timeout` | Validation exceeded the timeout window (default 150 seconds, configurable up to 300). Simplify the definition or reduce the scope of the source pattern. |
| `409 Conflict` | The requested change is blocked by the current state, most often: a Computed File with the same name already exists in the datastore, the source file has not been profiled, the update would drop fields that have attached quality checks or anomalies (use `force_drop_fields: true` to confirm), or the delete is blocked by dependents. The response body carries a `detail` describing the blocker and any `dropped_fields` or dependent IDs. |
| `422 Unprocessable Entity` | Payload does not match the schema: unknown container type, wrong field types, or a required top-level field is missing. |

*Last updated: October 21, 2026.*
