Assess a file
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
POST /v1/assessAuthorization: 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 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
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 | 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> | 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 | 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). |
| completedAt | string (date-time) | When processing finished (ISO 8601). Absent while the job is processing. |
Example response
{ "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)
{ "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)
{ "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, 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 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 (/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. When a verification specialist has reviewed a file (possible when an Idempotency-Key replay returns an earlier job), that file's result also carries an expertReview field; see ResultExpertReview for its shape.