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 these endpoints.
Reads also require cycle-admin access. A valid key without this access returns 403 Forbidden on the GET endpoints.
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 uses its externalKey to match an existing field. If neither identifier matches, the API creates a field.
  • An existing field omitted from the request is removed, and its values are archived.
  • A field whose type changes keeps its ID, but its old values are archived.
  • Changes to options or limits archive values that the new field definition does not accept.
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 fields in the same format as Get supplemental fields, plus an archivedValues list. Save each new field’s id for the values upload. Each archivedValues entry contains a fieldId and the count of values archived for that field. The field may have been removed by the request. The list is empty if no values were archived.

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 archived. Send the 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: