> ## Documentation Index
> Fetch the complete documentation index at: https://docs.facetflux.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Diagnostics

> Upload a catalog file and get a completeness report — fill rates, product types, and quality gaps.

The diagnostic is the fastest way to see what's in a catalog. You upload a CSV or Excel file, FacetFlux profiles it, and you get back a report: how complete each column is, what product types it found, ETIM readiness, and a few sample enrichments. The first runs per tenant are free.

It's a two-step flow: **start** a diagnostic (returns immediately with an id), then **poll** until it's done and fetch the report.

## Start a diagnostic

The upload is a **raw-body** `POST` — the request body *is* the file bytes, not multipart form data. Two headers matter:

* `Content-Type` — send the exact Excel MIME type `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` for `.xlsx`; **any other value is treated as CSV**.
* `Content-Disposition` — carries the filename, e.g. `attachment; filename="catalog.csv"`.

An optional `?language=` query parameter (`en` or `de`, default `en`) sets the report language.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://app.facetflux.com/api/v1/diagnostics?language=en" \
    -H "Authorization: Bearer $FACETFLUX_API_KEY" \
    -H "Content-Type: text/csv" \
    -H 'Content-Disposition: attachment; filename="catalog.csv"' \
    --data-binary @catalog.csv
  ```

  ```python Python theme={null}
  import os, requests

  with open("catalog.csv", "rb") as f:
      body = f.read()

  resp = requests.post(
      "https://app.facetflux.com/api/v1/diagnostics",
      params={"language": "en"},
      headers={
          "Authorization": f"Bearer {os.environ['FACETFLUX_API_KEY']}",
          "Content-Type": "text/csv",
          "Content-Disposition": 'attachment; filename="catalog.csv"',
      },
      data=body,
  )
  resp.raise_for_status()
  diagnostic_id = resp.json()["id"]
  ```
</CodeGroup>

A successful start returns `202 Accepted` with `{ "id": "...", "wasFree": true }`. The `id` identifies the report you'll poll for.

<Note>
  Only one diagnostic runs per tenant at a time — starting a second while one is active returns `409`. Starts are also rate-limited to a few per hour.
</Note>

## Poll for completion

Poll `GET /api/v1/diagnostics/{id}` until `status` is `completed` (or `failed`):

```python Python theme={null}
import time

while True:
    r = requests.get(
        f"https://app.facetflux.com/api/v1/diagnostics/{diagnostic_id}",
        headers={"Authorization": f"Bearer {os.environ['FACETFLUX_API_KEY']}"},
    ).json()
    if r["status"] in ("completed", "failed"):
        break
    print(r["phase"], r.get("progress"))
    time.sleep(2)
```

The poll response carries `status` (`queued` / `processing` / `completed` / `failed`), a human-readable `phase`, `progress`, and `error` if it failed.

## Read the report

Once completed, fetch the full report:

```bash theme={null}
curl "https://app.facetflux.com/api/v1/diagnostics/$DIAGNOSTIC_ID/report" \
  -H "Authorization: Bearer $FACETFLUX_API_KEY"
```

The report is built in phases, so the fields fill in as the analysis progresses:

* **Profile** — `averageCompleteness` (overall score) and `columns`, each with a `fillRate`, `rowsWithValue`, `uniqueValueCount`, and `topValues`.
* **Understand** — `typeGroups` (the product types it detected, each with its own completeness and per-attribute fill rates), a `qualityNarrative`, and structured `issues`.
* **ETIM readiness** — `etimReadiness` per product type: the matched ETIM class, coverage percentage, and `missingFeatures` the class expects.
* **Samples** — a few `samples` showing `before` values and AI `suggestions`, so you can see the kind of enrichment FacetFlux would produce.

`GET /api/v1/diagnostics` returns your ten most recent reports (a bare array, newest first).

<Card title="Turn gaps into values" icon="wand-magic-sparkles" href="/guides/enrichment">
  The diagnostic shows what's missing. Enrichment fills it — see the Enrichment guide.
</Card>
