# Forensics quickstart
Source: https://docs.file.ai/quickstart/forensics

> Check a stored file for signs of editing or AI generation, and read the result, in about 5 minutes.

fileForensics

# Forensics quickstart

Copy pageCopy page

Check a stored file for signs of editing or AI generation, and read the result, in about 5 minutes.

Copy pageCopy page

**What you’ll have at the end:** a finished fileForensics run on one of your files, with a per-check verdict you can act on.

**You need:** an API key and the `fileId` of a file already stored in your workspace. Don’t have one yet? Run the [API quickstart](/quickstart/api) first; its last step gives you a `fileId`. To check files as you upload them instead, see [Two ways to run it](/forensics/overview#two-ways-to-run-it).

1

Set your key

```
export FILEAI_API_KEY="your-api-key"
export FILEAI_BASE_URL="https://api.orion.file.ai/prod/v1"
export FILE_ID="your-file-id"
```

On a regional workspace, use your [regional host](/authentication#regions).

2

Start a run

Choose which checks to run in `spec_filter`. These three cover the common cases for scanned images and PDFs:

cURL

Python

Node.js

```
curl -X POST "$FILEAI_BASE_URL/forensics" \
  -H "x-api-key: $FILEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"file_id\": \"$FILE_ID\",
    \"spec_filter\": [\"copy_move\", \"ai_gen_detection\", \"magic_byte\"]
  }"
```

```
import os, requests

BASE = "https://api.orion.file.ai/prod/v1"
HEADERS = {"x-api-key": os.environ["FILEAI_API_KEY"]}

run = requests.post(
    f"{BASE}/forensics",
    headers=HEADERS,
    json={
        "file_id": os.environ["FILE_ID"],
        "spec_filter": ["copy_move", "ai_gen_detection", "magic_byte"],
    },
).json()
```

```
const BASE = "https://api.orion.file.ai/prod/v1";
const headers = { "x-api-key": process.env.FILEAI_API_KEY, "Content-Type": "application/json" };

const run = await fetch(`${BASE}/forensics`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    file_id: process.env.FILE_ID,
    spec_filter: ["copy_move", "ai_gen_detection", "magic_byte"],
  }),
}).then((r) => r.json());
```

The request returns `202 Accepted` straight away:

202 Accepted

```
{
  "run_id": "eebb50fee5b1",
  "file_id": "665f1c2e8a1b4c0012ab9911",
  "spec_filter": ["copy_move", "ai_gen_detection", "magic_byte"],
  "status": "queued",
  "poll_url": "https://api.orion.file.ai/prod/v1/forensics/runs/eebb50fee5b1",
  "created_at": "2026-09-25T02:14:00.000Z"
}
```

3

Poll until it finishes

fileForensics has no callback, so poll the `poll_url`:

cURL

Python

Node.js

```
curl "$FILEAI_BASE_URL/forensics/runs/<run_id>" -H "x-api-key: $FILEAI_API_KEY"
```

```
import time

while True:
    result = requests.get(f"{BASE}/forensics/runs/{run['run_id']}", headers=HEADERS).json()
    if result["status"] in ("complete", "error"):
        break
    time.sleep(5)
```

```
let result;
do {
  await new Promise((r) => setTimeout(r, 5000));
  result = await fetch(`${BASE}/forensics/runs/${run.run_id}`, { headers }).then((r) => r.json());
} while (!["complete", "error"].includes(result.status));
```

Stop when `status` is `complete` or `error`. Results appear under `forensics` as each check reports. If a run never finishes, see [Retry a stuck run](/verify/forensics#how-it-works).

## 

[​

](#read-the-result)

Read the result

Illustrative: shape from the API reference. Fields beyond `status` and `value` vary by check.

GET /v1/forensics/runs/eebb50fee5b1

```
{
  "run_id": "eebb50fee5b1",
  "file_id": "665f1c2e8a1b4c0012ab9911",
  "spec_filter": ["copy_move", "ai_gen_detection", "magic_byte"],
  "status": "complete",
  "created_at": "2026-09-25T02:14:00.000Z",
  "forensics": {
    "copy_move": { "status": "issue" },
    "ai_gen_detection": { "status": "none", "value": "AI-generated: false" },
    "magic_byte": { "status": "none" }
  }
}
```

Read each check’s `status`, not just the run’s: a `complete` run can still contain an `issue`.

`status`

Meaning

What to do

`none`

Checked, nothing found

Continue

`issue`

Checked, something found

Route the file for review

`skip`

Not applicable to this file type

Nothing; choose a check that fits the file

`error`

The check failed

Retry the run

In this example, `copy_move` found cloned regions in the image, so the file should go to a reviewer before its data is used.

## 

[​

](#what-to-try-next)

What to try next

## All detectors

Nineteen checks, from web presence to trailing data.

## Document integrity checks

Choose checks per document and route on the result.

## Run fileForensics from your assistant

The MCP server exposes `run_file_forensics`.

## Retries and stuck runs

What to do when a run doesn’t finish.

[Previous](/forensics/overview)[

Detectors & results

Run checks on a stored file for signs of editing, AI generation, reuse and hidden content.

Next

](/verify/forensics)

⌘I

## On this page

*   [Read the result](#read-the-result)
*   [What to try next](#what-to-try-next)
