# Assess a file

> Source: https://pixelsystems.com/docs/api/assess


Assess up to 20 files of one case in one multipart/form-data call and get a result for each file, or a 202 envelope if the wait window elapses.

`POST /v1/assess`

## Request

```http
POST /v1/assess
Authorization: Bearer {api_key}
Content-Type: multipart/form-data; boundary={set by your HTTP client}
```

## Header parameters

| Header | Type | Description | Required |
| --- | --- | --- | --- |
| Idempotency-Key | `string` | Optional idempotency key, 1–255 characters. Retrying a submission with the same key returns the original job instead of creating a duplicate. | No |

## Query parameters

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| wait | `boolean` | Block until the job completes. `true` (the default) waits up to 30 s before falling back to the polling response; `false` returns immediately. Changes response timing and HTTP status only, never the result shape. | No |

## Body parameters

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| file | `array<string (binary)>` | The files to assess, one `file` part each: 1 to 20 files of one case, all in one job with a result for each file. Each file is a JPEG, PNG or [HEIC/HEIF](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format) image of at most 25 MB, or a PDF of at most 2 MB. Each application's pipeline analyzes one kind of file, images or PDFs, and accepts only that kind; formats of that kind may be mixed. The request is accepted or refused whole: the first refused file is named in the error, and no job is made. Every file part is named `file`: more than 20 files, or a file part with another name, is `invalid_request`; a file of another kind is `unsupported_format`. | Yes |
| metadata | `string` | Optional JSON-encoded object, echoed back verbatim as `metadata` on the result, for all files of the request. One `metadata` part for each request; a second is `invalid_request`. At most 16 KiB when serialized as compact JSON in UTF-8: whitespace between values does not count, the contents of strings do. | No |

## Example request

```bash
curl -X POST "https://api.pixelsystems.com/v1/assess" \
  -H "Authorization: Bearer {api_key}" \
  -H "Idempotency-Key: case-123-photos" \
  -F "file=@front.jpg;type=image/jpeg" \
  -F "file=@side.jpg;type=image/jpeg" \
  -F 'metadata={"customer_case_id":"case-123"}'
```

## Response (200)

Assessment completed within the wait window

| Field | Type | Description |
| --- | --- | --- |
| jobId | `string (uuid)` | Single job identity, shared by every path that references this job. |
| status | [`JobStatus`](https://pixelsystems.com/docs/api/resources#jobstatus) | Lifecycle status of a job, derived from `results[].status`: `processing` while any result is processing, then `error` if any result errored, otherwise `completed`. A job with no files is `processing`. Each result carries its own `verdict` once `completed`. `rejected` is reserved for future use and is not currently produced. |
| results | [`array<FileResult>`](https://pixelsystems.com/docs/api/resources#fileresult) | One result per submitted file, in submission order. This is the job's result; read each entry's `status`, `verdict`, and `expertReview`. There is no job-level verdict: two files in one job can reach opposite assessments. |
| error | [`JobResultError`](https://pixelsystems.com/docs/api/resources#jobresulterror) | Why a job or one of its results ended in `error` (or a job in `rejected`) status; present only for those statuses. Uses the same code vocabulary as endpoint errors. On the job it is derived from the results: `result_unavailable` when every errored result carries that code, otherwise `internal_error`. |
| metadata | `object` | Caller-supplied metadata, echoed back verbatim. Absent when the submission carried none. |
| createdAt | `string (date-time)` | When the job was created ([ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). |
| completedAt | `string (date-time)` | When processing finished ([ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)). Absent while the job is `processing`. |

## Example response

```json
{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "results": [
    {
      "file": {
        "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "name": "front.jpg",
        "size": 1834211,
        "type": "image/jpeg"
      },
      "status": "completed",
      "startedAt": "2026-06-01T12:00:01Z",
      "completedAt": "2026-06-01T12:00:14Z",
      "verdict": { "assessment": "ai_generated", "confidence": "high" },
      "dimensions": [
        {
          "key": "ai_detection",
          "assessment": "likely_ai",
          "score": 0.82,
          "confidence": "high"
        }
      ]
    },
    {
      "file": {
        "id": "9b2d3f41-6a8e-4c1b-9f0d-2e7a5c8b1d34",
        "name": "side.jpg",
        "size": 1650372,
        "type": "image/jpeg"
      },
      "status": "completed",
      "startedAt": "2026-06-01T12:00:01Z",
      "completedAt": "2026-06-01T12:00:12Z",
      "verdict": { "assessment": "authentic", "confidence": "medium" },
      "dimensions": [
        {
          "key": "ai_detection",
          "assessment": "likely_authentic",
          "score": 0.12,
          "confidence": "medium"
        }
      ]
    }
  ],
  "metadata": { "customer_case_id": "case-123" },
  "createdAt": "2026-06-01T12:00:00Z",
  "completedAt": "2026-06-01T12:00:14Z"
}
```

### Still processing (202)

```json
{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "results": [
    {
      "file": {
        "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "name": "photo.jpg",
        "size": 1834211,
        "type": "image/jpeg"
      },
      "status": "processing",
      "startedAt": "2026-06-01T12:00:01Z",
      "completedAt": null
    }
  ],
  "createdAt": "2026-06-01T12:00:00Z"
}
```

### Failed (200)

```json
{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "error",
  "results": [
    {
      "file": {
        "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "name": "photo.jpg",
        "size": 1834211,
        "type": "image/jpeg"
      },
      "status": "error",
      "startedAt": "2026-06-01T12:00:01Z",
      "completedAt": "2026-06-01T12:00:09Z",
      "error": {
        "code": "internal_error",
        "message": "The analysis failed during processing."
      }
    }
  ],
  "error": {
    "code": "internal_error",
    "message": "The job failed during processing."
  },
  "createdAt": "2026-06-01T12:00:00Z",
  "completedAt": "2026-06-01T12:00:09Z"
}
```

## Notes

The request body is [`multipart/form-data`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/POST), not JSON: send each file as a `file` form part, and the optional `metadata` as one more part, as in the example above.

### Several files in one call

Send up to 20 files that belong to one case, such as photos of one damage from different angles, as repeated `file` parts. They make one job with a result for each file, and each file is analysed and billed on its own; nothing is combined into a job-level verdict. Formats of the application's kind can be mixed, for example JPEG and HEIC. One `metadata` part applies to every file of the request, and one `Idempotency-Key` covers the whole request. The results do not keep the order you sent the files in: match each result to its file by `file.name`.

A request is accepted or refused whole. If any file is refused, no job is made and nothing is billed, and the files already stored are deleted: send the full request again after you fix it. For a refused file type or size, the error names the first refused file in its message and in `details.issues[].path`, as `file.0` for the first `file` part, `file.1` for the second, and so on. The other refusals name the part instead: `file` for too many files, `metadata` for a second `metadata` part, and the part's own name for a file part not named `file`.

| Problem | Status | Code |
| --- | --- | --- |
| More than 20 files, a second `metadata` part, or a file part not named `file` | `400` | `invalid_request` |
| A file of a type the application's pipeline does not accept | `400` | `unsupported_format` |
| A file over its size limit (25 MB for an image, 2 MB for a PDF) | `413` | `file_too_large` |

The [rate limit](https://pixelsystems.com/docs/api/rate-limits) counts the request once, whatever the number of files. With `wait=true`, the call waits for every file of the job; files still running when the wait window elapses show as `processing` in a `202` response.

### Reading the result

A `200` response carries a terminal status (`completed` or `error`); a `202` means the wait window elapsed first. Poll [Get a job's result](https://pixelsystems.com/docs/api/get-job) (`/v1/jobs/{jobId}`), which supports the same `wait` parameter, until the status leaves `processing`. The job's result is `results[]`, one entry per file, each with its own `status` and, once `completed`, its `verdict` and `dimensions`; see [`FileResult`](https://pixelsystems.com/docs/api/resources#fileresult). When a [verification specialist has reviewed](https://pixelsystems.com/docs/concepts#expert-review) a file (possible when an [`Idempotency-Key`](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/) replay returns an earlier job), that file's result also carries an `expertReview` field; see [`ResultExpertReview`](https://pixelsystems.com/docs/api/resources#resultexpertreview) for its shape.
