Skip to main content
Attaches your own per-employee data points to a performance review cycle—job level, tenure, sales attainment, external metrics—so they surface in calibration grids, tables, packets, and pre-read reports. See Supplemental data for how the data appears in the product.
The model is deliberately simple: fields are the cycle’s column definitions, values are per-employee data for those fields. Both PUTs are idempotent full replaces—send the complete desired state and the API reconciles it. Each PUT runs in a single transaction: a request either fully applies or fully fails.

Authorization

These endpoints enforce two rules beyond a valid API key:
Cycle admin only. The member who created the API key must be an admin of the cycle. Cycle admin is granted per cycle; it is not a company-wide role. A valid key without cycle-admin access returns 403 Forbidden on the PUT endpoints.
Reads are filtered, not rejected. The GET endpoints never return 403—a valid key without cycle-admin access on the cycle gets empty results instead. An unknown or cross-company cycleId returns 404 Not Found.
Supplemental values are treated as sensitive: request bodies sent to the values PUT are redacted from Windmill’s operation logs.

Path parameters

All four endpoints share the same path parameters.
string
required
Your company’s slug. Find it in your Dashboard URL, right after /s/:https://app.gowindmill.com/s/{companySlug}/home
string
required
The id of the review cycle. Open the cycle in the Dashboard and take the last segment of the URL:https://app.gowindmill.com/s/{companySlug}/performance-reviews/{cycleId}

Set supplemental fields

Replaces the cycle’s supplemental field definitions as a full set:
  • A field with an id is updated in place, keeping its id and its existing values. Label-only edits never touch data.
  • A field without an id is created.
  • An existing field omitted from the request is removed, along with its values.
  • A field whose type changes keeps its id, but its old values are deleted since they no longer fit the new type.
Field display order follows the request array. The list must be non-empty—to remove the last field, delete the cycle’s supplemental data rather than sending an empty list.

Body parameters

object[]
required
The complete ordered set of supplemental fields (at least one). Every field shares a core shape plus type-specific configuration.

Response

Returns the resulting field definitions—the same shape as Get supplemental fields—so you can capture the generated id of each new field for the values upload.

Get supplemental fields

Returns the cycle’s current supplemental field definitions, in order. A cycle with no supplemental data returns an empty fields array.

Response

object[]
required
The cycle’s supplemental fields, in order.Each field also carries the same type-specific properties documented under Set supplemental fields: options for SINGLE_SELECT, choices for MULTI_SELECT, min/max/step/integer/labels for SCALE, min/max/integer/unit for NUMERIC, labels for NPS, and points for RATING.

Set supplemental values

Sets supplemental values for one or more employees. Define fields first—writing values before any fields exist returns 400 Bad Request. Each listed employee is a full replace: fields omitted from that employee’s values map have their existing values deleted, so always send an employee’s complete data. Employees not listed in the request are left completely untouched, which makes incremental batch uploads safe.

Body parameters

object[]
required
The employees to write.

Response

integer
required
Number of employees whose values were written.

Get supplemental values

Returns a page of employees with their current supplemental values, ordered by employee id. Employees with no values for this cycle are not included.

Query parameters

integer
default:"50"
Maximum number of employees to return, up to 200.
string
Opaque pagination cursor from a previous page’s nextCursor. Omit for the first page.

Response

object[]
required
The page of employees with supplemental values.
string | null
Cursor for the next page; null or absent when there are no more.

Value shape

Every value—written by the values PUT and returned by the values GET—is an object discriminated by type, which must match the field’s type:
  • TEXT{ "type": "TEXT", "value": string }
  • SINGLE_SELECT{ "type": "SINGLE_SELECT", "value": string, "freeText": string | null } (value matches an option’s value)
  • MULTI_SELECT{ "type": "MULTI_SELECT", "value": string[] } (at least one choice value)
  • BOOLEAN{ "type": "BOOLEAN", "value": boolean, "freeText": string | null }
  • SCALE / NUMERIC / RATING{ "type": "SCALE" | "NUMERIC" | "RATING", "value": number } (a RATING value must match one of the field’s points)
  • NPS{ "type": "NPS", "value": number } (an integer from 0–10)
Changing supplemental fields or values marks any generated calibration pre-read report as stale so facilitators know to regenerate it. See Updating data mid-cycle.

Errors

The error response body is always shaped like this: