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

# Get backfill status

> Poll a performance-review backfill import job to follow it through validation, import, and completion.

Returns the current status and step-by-step history of a backfill import job started with [Create a backfill](/api-reference/performance-reviews/create-backfill). Poll it with the `jobId` from that call until the job reports `COMPLETED` or `FAILED`.

```
GET /api/v2/c/{companySlug}/cycles/backfills/{jobId}
```

Imports usually finish quickly, but the work is asynchronous. A `201` from the create call means that the payload passed validation. Windmill queues new and `VALIDATED` jobs. An idempotent replay of an `IMPORTING`, `COMPLETED`, or `FAILED` job returns the original IDs but does not queue the job again. Once the job reports `COMPLETED`, the imported cycle is fully materialized and you can read it back with [Export cycle packets](/api-reference/performance-reviews/export-cycle-packets).

## Authorization

This endpoint enforces one rule beyond a valid [API key](/api-reference/overview#authentication):

<Warning>
  **Admins and HR Admins only.** The member who created the API key must hold the `performance_reviews.manage_cycles` capability, which the **Admin** and **HR Admin** roles grant. A valid key without the capability returns `403 Forbidden`.
</Warning>

## Path parameters

<ParamField path="companySlug" type="string" required>
  Your company's slug. Find it in your Dashboard URL, right after `/s/`:

  `https://app.gowindmill.com/s/{companySlug}/home`
</ParamField>

<ParamField path="jobId" type="string" required>
  The import job's id, returned as `jobId` by [Create a backfill](/api-reference/performance-reviews/create-backfill).
</ParamField>

## Response

<ResponseField name="jobId" type="string" required>
  The import job's id.
</ResponseField>

<ResponseField name="status" type="string" required>
  The job's overall status:

  * **`VALIDATED`** — the payload passed validation and the import has not started. This status does not confirm that queueing succeeded.
  * **`IMPORTING`** — the import is running; `steps` shows how far it has gotten.
  * **`COMPLETED`** — the cycle imported successfully. Terminal.
  * **`FAILED`** — the import stopped with an error; see `error` and the failed step's `detail`. Terminal. To re-run, submit a new create call with a different `idempotencyKey`.
</ResponseField>

<ResponseField name="cycleId" type="string | null" required>
  The id of the cycle this job is importing — the same `cycleId` the create call returned.
</ResponseField>

<ResponseField name="steps" type="object[]" required>
  Step history in chronological order. Steps appear as the import reaches them, so a queued job can have an empty list. Step names include `cycle` (the cycle shell, reviewees, and questions), one `stage:{type}` entry per imported stage (for example `stage:SELF`, `stage:MANAGER`), `calibration` and `releases` when the payload included them, and `complete`.

  <Expandable title="step properties">
    <ResponseField name="step" type="string" required>The step's name, for example `cycle` or `stage:MANAGER`.</ResponseField>
    <ResponseField name="status" type="string" required>The step's status: `PENDING`, `RUNNING`, `COMPLETED`, or `FAILED`.</ResponseField>
    <ResponseField name="startedAt" type="string | null" required>ISO-8601 time the step started; `null` if it hasn't.</ResponseField>
    <ResponseField name="completedAt" type="string | null" required>ISO-8601 time the step finished (successfully or not); `null` while pending or running.</ResponseField>
    <ResponseField name="detail" type="object | null" required>Step-specific detail, such as counts of imported records or failure context; `null` when there's none.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="error" type="object | null" required>
  Failure information when `status` is `FAILED`; `null` otherwise.
</ResponseField>

<ResponseField name="completedAt" type="string | null" required>
  ISO-8601 time the job reached a terminal status (`COMPLETED` or `FAILED`); `null` while it's still queued or running.
</ResponseField>

## Errors

| Status             | When                                                                                                                       |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `401 Unauthorized` | The API key is missing, malformed, expired, or revoked.                                                                    |
| `403 Forbidden`    | The key is valid but its creator doesn't hold the `performance_reviews.manage_cycles` capability (Admin or HR Admin role). |
| `404 Not Found`    | No import job with this `jobId` exists in your company, or the job isn't a performance-review backfill.                    |

The error response body is always shaped like this:

```json theme={null}
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Backfill job not found",
    "requestId": "019f2986-8ea9-79ff-8ac2-612072b7cca6"
  }
}
```

<RequestExample>
  ```bash Poll a backfill job theme={null}
  curl \
    'https://api.gowindmill.com/api/v2/c/acme/cycles/backfills/019f4a10-3c2e-7a91-b6d4-1f0a2c9e8b77' \
    -H 'Authorization: Bearer wm_api_xxxxxxxxxxxxxxxxxxxx'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK (completed) theme={null}
  {
    "jobId": "019f4a10-3c2e-7a91-b6d4-1f0a2c9e8b77",
    "status": "COMPLETED",
    "cycleId": "019f4a10-3c2d-7f42-a8c1-9b6e5d3a2f10",
    "steps": [
      {
        "step": "cycle",
        "status": "COMPLETED",
        "startedAt": "2026-07-21T17:02:04.000Z",
        "completedAt": "2026-07-21T17:02:06.000Z",
        "detail": null
      },
      {
        "step": "stage:SELF",
        "status": "COMPLETED",
        "startedAt": "2026-07-21T17:02:06.000Z",
        "completedAt": "2026-07-21T17:02:08.000Z",
        "detail": null
      },
      {
        "step": "stage:MANAGER",
        "status": "COMPLETED",
        "startedAt": "2026-07-21T17:02:08.000Z",
        "completedAt": "2026-07-21T17:02:11.000Z",
        "detail": null
      },
      {
        "step": "releases",
        "status": "COMPLETED",
        "startedAt": "2026-07-21T17:02:11.000Z",
        "completedAt": "2026-07-21T17:02:12.000Z",
        "detail": null
      },
      {
        "step": "complete",
        "status": "COMPLETED",
        "startedAt": "2026-07-21T17:02:12.000Z",
        "completedAt": "2026-07-21T17:02:12.000Z",
        "detail": null
      }
    ],
    "error": null,
    "completedAt": "2026-07-21T17:02:12.000Z"
  }
  ```

  ```json 200 OK (importing) theme={null}
  {
    "jobId": "019f4a10-3c2e-7a91-b6d4-1f0a2c9e8b77",
    "status": "IMPORTING",
    "cycleId": "019f4a10-3c2d-7f42-a8c1-9b6e5d3a2f10",
    "steps": [
      {
        "step": "cycle",
        "status": "COMPLETED",
        "startedAt": "2026-07-21T17:02:04.000Z",
        "completedAt": "2026-07-21T17:02:06.000Z",
        "detail": null
      },
      {
        "step": "stage:SELF",
        "status": "RUNNING",
        "startedAt": "2026-07-21T17:02:06.000Z",
        "completedAt": null,
        "detail": null
      }
    ],
    "error": null,
    "completedAt": null
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "error": {
      "code": "NOT_FOUND",
      "message": "Backfill job not found",
      "requestId": "019f2986-8ea9-79ff-8ac2-612072b7cca6"
    }
  }
  ```
</ResponseExample>
