# Analyses

> Create image and document analyses, wait for the result with Prefer: wait or poll, cancel, download the PDF report and read the report object and verification page.

Source: https://fileverity.com/en/docs/analyses

An analysis is one image or document check. It checks one uploaded file with `fvimg-2` (images) or `fvdoc-3` (documents). Upload the file first (see [Files](https://fileverity.com/en/docs/files.md)), then create the analysis.

For a product overview with a request and a response, see the [AI image detection API](https://fileverity.com/en/api/ai-image-detection) and [Document fraud detection API](https://fileverity.com/en/api/document-fraud-detection).

## Create an analysis

`POST /analyses` returns the analysis object. The price is reserved when the analysis is created and released if it fails or is canceled.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | `fvimg-2` or `fvdoc-3`, or a snapshot ID such as `fvimg-2-2026-10-30`. |
| `file` | string | Yes | ID of an uploaded file. |
| `metadata` | object | No | Up to 16 string pairs. Keys up to 40 characters, values up to 500. Returned on the analysis and in webhooks. |
| `options.password` | string | No | Password of an encrypted document, up to 256 characters. |
| `options.locale` | string | No | Language of document findings: `en`, `fr`, `de`, `es`, `zh`, `vi` or `ar`. Default `en`. |

**Create an analysis: cURL**

```bash
curl https://api.fileverity.com/v1/analyses \
  -H "Authorization: Bearer $FILEVERITY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0e5f6a64-52c2-4b36-9d0d-6f3d2f4c9a10" \
  -d '{"model": "fvdoc-3", "file": "file_Pw3mY6zB8Hk2pQ4vN9sL1xR7", "metadata": {"order_id": "A-10492"}}'
```

**Create an analysis: JavaScript**

```javascript
const response = await fetch("https://api.fileverity.com/v1/analyses", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.FILEVERITY_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({ model: "fvdoc-3", file: file.id, metadata: { order_id: "A-10492" } }),
});
const analysis = await response.json();
```

**Create an analysis: Python**

```python
import os
import uuid
import requests

response = requests.post(
    "https://api.fileverity.com/v1/analyses",
    headers={
        "Authorization": "Bearer " + os.environ["FILEVERITY_API_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"model": "fvdoc-3", "file": file["id"], "metadata": {"order_id": "A-10492"}},
)
analysis = response.json()
```

## Wait, poll or use webhooks

Create returns `202` with `status: processing`. You have three ways to get the result:

- **Wait.** Send `Prefer: wait=N`, where N is up to 60 seconds. The request is held until the analysis finishes or N seconds pass. A finished analysis returns `200`; otherwise you still get `202`. The response header `preference-applied: wait=N` confirms the wait.
- **Poll.** Call `GET /analyses/{id}` every few seconds until `status` is no longer `processing`.
- **Webhook.** Subscribe to `analysis.completed` and `analysis.failed` and fetch the analysis when the event arrives. See [Webhooks](https://fileverity.com/en/docs/webhooks.md).

**Poll until done: JavaScript**

```javascript
let analysis = created;
while (analysis.status === "processing") {
  await new Promise((resolve) => setTimeout(resolve, 3000));
  const response = await fetch("https://api.fileverity.com/v1/analyses/" + analysis.id, {
    headers: { Authorization: "Bearer " + process.env.FILEVERITY_API_KEY },
  });
  analysis = await response.json();
}
```

**Poll until done: Python**

```python
import time

analysis = created
while analysis["status"] == "processing":
    time.sleep(3)
    analysis = requests.get("https://api.fileverity.com/v1/analyses/" + analysis["id"], headers=headers).json()
```

**202 Accepted, document**

```json
{
  "id": "an_8Hk2pQ4vN9sL1xR7cW3mY6zB",
  "object": "analysis",
  "created": 1793700120,
  "model": "fvdoc-3-2026-11-03",
  "livemode": true,
  "status": "processing",
  "file": {
    "name": "contract.pdf",
    "bytes": 18874368,
    "format": "pdf"
  },
  "report_id": null,
  "report_url": null,
  "summary": null,
  "result": null,
  "error": null,
  "progress": {
    "stage": "queued",
    "done": 0,
    "total": 0,
    "position": 1
  },
  "usage": {
    "amount": 599,
    "currency": "usd",
    "funding": "balance"
  },
  "metadata": {
    "order_id": "A-10492"
  },
  "completed_at": null,
  "expires_at": null
}
```

While an analysis is processing, `progress.stage` is one of `queued`, `reading`, `analyzing` or `reporting`, and `position` is the queue position while the stage is `queued`. Documents can be queued. Waiting is optional for documents and can end before the result is ready, so always handle `202`.

## When the service is busy

When capacity is busy, `POST /analyses` returns `503` with the code `service_unavailable` and a `retry-after` header. You are not charged. Wait the number of seconds in `retry-after`, then retry with the same `Idempotency-Key` so the retry can never run a check twice. For the best throughput, run image analyses one after another instead of in parallel.

**Retry when busy: JavaScript**

```javascript
async function create(body, key) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const response = await fetch("https://api.fileverity.com/v1/analyses", {
      method: "POST",
      headers: { Authorization: "Bearer " + process.env.FILEVERITY_API_KEY, "Content-Type": "application/json", "Idempotency-Key": key },
      body: JSON.stringify(body),
    });
    if (response.status !== 503) return response;
    const wait = Number(response.headers.get("retry-after")) || 15;
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }
  throw new Error("Service busy");
}
```

**Retry when busy: Python**

```python
import time

def create(body, key):
    for attempt in range(5):
        response = requests.post("https://api.fileverity.com/v1/analyses", headers={**headers, "Idempotency-Key": key}, json=body)
        if response.status_code != 503:
            return response
        time.sleep(int(response.headers.get("retry-after", 15)))
    raise RuntimeError("Service busy")
```

## Statuses

| Status | Meaning |
| --- | --- |
| `processing` | The analysis is running. `result` is `null`. |
| `succeeded` | The result is ready. `report_id`, `report_url`, `summary` and `result` are set. |
| `failed` | The analysis could not be completed. `error` holds the `code` and `message`. You were not charged. |
| `canceled` | You canceled it. `error.code` is `canceled`. You were not charged. |

**Failed analysis**

```json
{
  "id": "an_8Hk2pQ4vN9sL1xR7cW3mY6zB",
  "object": "analysis",
  "created": 1793700120,
  "model": "fvimg-2-2026-10-30",
  "livemode": true,
  "status": "failed",
  "file": {
    "name": "claim-photo.jpg",
    "bytes": 2481920,
    "format": "jpeg"
  },
  "report_id": null,
  "report_url": null,
  "summary": null,
  "result": null,
  "error": {
    "code": "analysis_failed",
    "message": "The analysis could not be completed. You were not charged; retry it, and contact support with the request ID if it fails again."
  },
  "progress": null,
  "usage": {
    "amount": 0,
    "currency": "usd",
    "funding": null
  },
  "metadata": null,
  "completed_at": 1793700135,
  "expires_at": null
}
```

## The report object

A succeeded analysis carries the same report that the website, the PDF and the verification page show.

| Field | Description |
| --- | --- |
| `report_id` | Report ID, `FV-XXXX-XXXX-XXXX`. |
| `report_url` | Public verification page for the report. Anyone with the link can check the report ID, the file fingerprint and the timestamp against the record, without an account. |
| `summary` | The short verdict. See below. |
| `result.report` | The full report. `result.report.schema` is `fileverity.report/1` for images and `fileverity.docpro/1` for documents. |
| `result.stamped_at` | Unix timestamp of the RFC 3161 timestamp issued for the report, or `null`. |
| `usage` | `amount` charged in cents (0 for free-start checks and failures), `currency` and `funding` (`trial` or `balance`). |
| `metadata` | The metadata you sent, or `null`. |
| `expires_at` | Unix timestamp after which `result` is removed. Results are kept for 7 days. |

**200 OK, image**

```json
{
  "id": "an_8Hk2pQ4vN9sL1xR7cW3mY6zB",
  "object": "analysis",
  "created": 1793700120,
  "model": "fvimg-2-2026-10-30",
  "livemode": true,
  "status": "succeeded",
  "file": {
    "name": "claim-photo.jpg",
    "bytes": 2481920,
    "format": "jpeg"
  },
  "report_id": "FV-7K2M-9QXD-4T8R",
  "report_url": "https://fileverity.com/verify/FV-7K2M-9QXD-4T8R",
  "summary": {
    "level": "L2",
    "assessment": "authentic_untouched",
    "action": "accept",
    "ai": "very_low",
    "edit": "none"
  },
  "result": {
    "report": {
      "schema": "fileverity.report/1",
      "id": "FV-7K2M-9QXD-4T8R"
    },
    "stamped_at": 1793700130
  },
  "error": null,
  "progress": null,
  "usage": {
    "amount": 199,
    "currency": "usd",
    "funding": "balance"
  },
  "metadata": {
    "order_id": "A-10492"
  },
  "completed_at": 1793700131,
  "expires_at": 1794304931
}
```

The `report` object is shortened above; the real one holds the complete findings.

The `summary` is the stable, machine-readable verdict. `result.report` is the full report that the website and the PDF render. Its structure follows the schema named in `result.report.schema`. Within the same schema version, listed fields keep their name and meaning, and new fields can be added, so ignore fields you do not recognize.

### Image summary

| Field | Values |
| --- | --- |
| `level` | `L0` Undetermined, `L1` Source verified, `L2` No anomalies found, `L3` Needs manual review, `L4` Highly suspicious, `L5` Strong evidence against authenticity. |
| `action` | `accept`, `review`, `reject`, `resubmit` |
| `assessment` | `authentic_untouched`, `authentic_locally_edited`, `ai_generated`, `undetermined` |
| `ai` | `labelled`, `very_likely`, `suspected`, `undecided`, `low`, `very_low`, `not_evaluated` |
| `edit` | `detected`, `suspected`, `suspected_record`, `review`, `generative`, `minor`, `none`, `not_applicable`, `not_evaluated` |

### Document summary

| Field | Description |
| --- | --- |
| `kind` | Always `docpro`. |
| `level` | `L0` Undetermined, `L1` Source verified, `L2` No signs of tampering, `L3` Needs manual review, `L4` High risk, `L5` Problems established. |
| `action` | `accept`, `note` (check the AI content marked in the report against your policy), `review`, `reject` or `resubmit`. |
| `reason` | The finding or condition behind the level, for example `clean`, `incomplete` or a finding ID, or `null`. |
| `findings` | IDs of the findings that matter. |
| `pages`, `images` | Pages in the document and embedded photos examined. |
| `text` | The verdict on the document's text (`ai_generated`, `mixed`, `human`, `inconclusive`), or `null` when text was not evaluated. |
| `strong` | `true` when a listed finding is strong. Absent otherwise. |

**200 OK, document**

```json
{
  "id": "an_Zc4vB7nP1sK8tL3wR6yQm2Xa",
  "object": "analysis",
  "created": 1793700300,
  "model": "fvdoc-3-2026-11-03",
  "livemode": true,
  "status": "succeeded",
  "file": {
    "name": "contract.pdf",
    "bytes": 18874368,
    "format": "pdf"
  },
  "report_id": "FV-3PXH-6WQ2-8NDC",
  "report_url": "https://fileverity.com/verify/FV-3PXH-6WQ2-8NDC",
  "summary": {
    "kind": "docpro",
    "level": "L3",
    "action": "review",
    "reason": null,
    "findings": [],
    "pages": 12,
    "images": 3,
    "text": "human"
  },
  "result": {
    "report": {
      "schema": "fileverity.docpro/1",
      "id": "FV-3PXH-6WQ2-8NDC"
    },
    "stamped_at": 1793700340
  },
  "error": null,
  "progress": null,
  "usage": {
    "amount": 599,
    "currency": "usd",
    "funding": "balance"
  },
  "metadata": null,
  "completed_at": 1793700341,
  "expires_at": 1794305141
}
```

### Image report fields

`result.report` of an image analysis. Nested objects are listed in the [API reference](https://fileverity.com/en/docs/api-reference.md#create-analysis).

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `schema` | `"fileverity.report/1"` | Yes |  |
| `id` | string | Yes | Report ID, FV-XXXX-XXXX-XXXX. |
| `sample` | `true` | No | Present on the fixed sample reports that test keys return. |
| `issuedAt` | string | No | When the report was issued, ISO 8601 in UTC. Format date-time. |
| `issuer` | object | No | Who issued the report. |
| `file` | object | No | The checked image. |
| `pro` | object | No | The verdict. The same values as summary, with reason. |
| `score` | integer or null | No | Authenticity score from 0 to 100 in steps of 5: how strongly the evidence supports the image as an authentic, unedited capture. null when not evaluated. |
| `confidence` | one of `"high"`, `"medium"`, `"low"` | No | How much this file allows the analysis to establish. Low confidence usually reflects the file itself, such as low resolution or heavy compression. |
| `questions` | object | No | The three questions the report answers. |
| `generator` | object or null | No | The AI generator named for the image, or null. |
| `regions` | array of object | No | Flagged regions, in pixels of the displayed image. |
| `provenance` | object | No | What the file's own records say about its origin. |
| `digest` | string | No | Fingerprint of the report content, used by the verification page. |

### Document report fields

`result.report` of a document analysis.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `schema` | `"fileverity.docpro/1"` | Yes |  |
| `id` | string | Yes | Report ID, FV-XXXX-XXXX-XXXX. |
| `sample` | `true` | No | Present on the fixed sample reports that test keys return. |
| `issuedAt` | string | No | When the report was issued, ISO 8601 in UTC. Format date-time. |
| `receivedAt` | string | No | When the file was received, ISO 8601 in UTC. Format date-time. |
| `issuer` | object | No | Who issued the report. |
| `file` | object | No | The checked document. |
| `level` | one of `"L0"`, `"L1"`, `"L2"`, `"L3"`, `"L4"`, `"L5"` | No | Risk level, L0 to L5. |
| `action` | one of `"accept"`, `"note"`, `"review"`, `"reject"`, `"resubmit"` | No |  |
| `reason` | string or null | No | The finding or condition behind the level. |
| `findings` | array of object | No | The findings behind the level, each with the pages and embedded photos it refers to. |
| `text` | object | No | The result for the document's text. |
| `images` | array of object | No | The embedded photos and graphics, numbered in document order. |
| `structure` | object or null | No | The edit history of the file, or null when it was not examined. |
| `provenance` | object | No | What the file's own records say about its origin. |
| `digest` | string | No | Fingerprint of the report content, used by the verification page. |

## Retrieve, list and cancel

- `GET /analyses/{id}` returns the analysis, with its result while it is kept.
- `GET /analyses` lists analyses of the workspace, newest first. `limit` is 1 to 100 (default 20), `type` filters by `image` or `document`, and `starting_after` takes the previous page's `next_cursor`. List items never include `result`.
- `POST /analyses/{id}/cancel` cancels a document analysis that is still processing and releases its charge. Image analyses cannot be canceled; a late or finished cancel returns `not_cancelable`.

**List and cancel: cURL**

```bash
curl "https://api.fileverity.com/v1/analyses?limit=20&type=document" \
  -H "Authorization: Bearer $FILEVERITY_API_KEY"

curl -X POST https://api.fileverity.com/v1/analyses/an_8Hk2pQ4vN9sL1xR7cW3mY6zB/cancel \
  -H "Authorization: Bearer $FILEVERITY_API_KEY"
```

## PDF report

`GET /analyses/{id}/report.pdf` downloads the sealed PDF report of a succeeded analysis while its result is kept. Add `?locale=` with `en`, `fr`, `de`, `es`, `zh`, `vi` or `ar` (default `en`). Send the `Authorization` header; the response is `application/pdf`.

**Download the PDF: cURL**

```bash
curl "https://api.fileverity.com/v1/analyses/an_8Hk2pQ4vN9sL1xR7cW3mY6zB/report.pdf?locale=en" \
  -H "Authorization: Bearer $FILEVERITY_API_KEY" \
  -o Fileverity-FV-7K2M-9QXD-4T8R.pdf
```

For documents, page images and text excerpts are kept for 3 hours after the check. After that the PDF omits them, and the response header `x-evidence-included` is `false`. Download the PDF when the analysis finishes if you need it to include the evidence.

## Errors

| Code | Status | Cause |
| --- | --- | --- |
| `file_not_found` | 400 | The file ID is unknown, expired or already used, or the upload to `upload.url` has not finished yet. |
| `model_not_found` | 404 | Unknown model. |
| `invalid_parameter` | 400 | A field is missing, mistyped or unknown. Text models belong to `POST /text/detections`. |
| `unsupported_file`, `invalid_file` | 415, 422 | The file format is not supported for the model, or the file is damaged. |
| `file_too_large`, `too_many_pixels`, `too_many_pages`, `document_too_long` | 413 | A size limit was exceeded. |
| `password_required`, `wrong_password` | 422 | The document is encrypted. Send `options.password`. |
| `not_examinable` | 422 | The document has nothing that can be examined. Not charged. |
| `insufficient_balance`, `spend_limit_reached` | 402 | The balance or the key's monthly limit cannot cover the price. |
| `service_unavailable`, `analysis_failed` | 503, 502 | A temporary failure or busy capacity. Not charged; retry. `service_unavailable` carries a `retry-after` header. See [When the service is busy](https://fileverity.com/en/docs/analyses.md#busy). |
| `unsupported_media_type` | 415 | The body is not sent as `application/json`. |
| `idempotency_mismatch`, `idempotency_in_progress` | 409 | The `Idempotency-Key` was used with a different body, or its first request is still running. See [Idempotency](https://fileverity.com/en/docs/idempotency.md). |
| `not_cancelable` | 409 | Only documents that are still processing can be canceled. |

> **Note.** Some failures happen after the analysis is created, for example when a model cannot finish. They set `status: failed` and `error` on the analysis instead of failing the create call, and the `analysis.failed` webhook fires. See [Errors](https://fileverity.com/en/docs/errors.md).

