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

# Candidate preferences

> Read and replace the recruiter-entered work preferences that drive ParaAI matching.

A candidate's work preferences (locations, compensation, work authorization, and so on) are sent as the required `preferences` object when you [submit a candidate](/agency/guides/submit-candidates). Afterwards you can read them one at a time or in batches, and replace them per candidate. These are the same values recruiters manage in the app, and they always take precedence in ParaAI matching.

## Required fields

| Field | Rule |
| - | - |
| `locations` | Non-empty array of location keys, for example `san_francisco` |
| `workplaceTypes` | Non-empty array of `REMOTE`, `ON_SITE`, `HYBRID` |
| `salaryMin` | Positive integer, annual |
| `idealFundingRounds` | Non-empty array, for example `SERIES_A` |
| `visaAuthorization` | One of the three values below |

Optional fields are `salaryMax`, `ote`, `roleType`, `excludedIndustries`, and `companySizePreferences`. The complete list of allowed values for each field is on the [replace preferences](/agency/api-reference/replace-talent-network-candidate-preferences) reference page.

`visaAuthorization` is the only work-authorization field:

| Value | In-app label | Covers, for example |
| - | - | - |
| `NO_VISA_AUTHORIZATION_NEEDED` | No visa required | US citizen, Green Card holder |
| `HAS_VISA_AUTHORIZATION` | Requires visa transfer | OPT, H-1B transfers |
| `NEEDS_NEW_VISA_AUTHORIZATION` | Requires sponsorship | New H-1B, TN |

## Read one candidate

[`GET /talent-network/candidates/{candidateUserId}/preferences`](/agency/api-reference/get-talent-network-candidate-preferences) returns `{ candidateUserId, preferences }`.

```bash theme={null}
curl "https://external-api.paraform.com/api/external/v1/talent-network/candidates/$CANDIDATE_USER_ID/preferences" \
  -H "Authorization: Bearer $PARAFORM_API_KEY"
```

* `preferences` is `null` when the candidate has none yet. This can happen for candidates who entered the network through a role submission.
* Any candidate owned by an approved member of your agency is addressable, not just the current membership list. That way you can update off-market or expired candidates before re-enabling them.
* Another agency's `candidateUserId` answers `404`, the same as an id that doesn't exist.

## Read many candidates

[`POST /talent-network/candidates/preferences/lookup`](/agency/api-reference/lookup-talent-network-candidate-preferences) reads up to 50 candidates in one call. Reference each candidate by `candidateUserId`, or by an identity your systems already hold (`email` or `linkedinUrl`):

```bash theme={null}
curl -X POST "https://external-api.paraform.com/api/external/v1/talent-network/candidates/preferences/lookup" \
  -H "Authorization: Bearer $PARAFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "candidates": [
      { "candidateUserId": "clx8k2j3a0000qz8h1b2c3d4e" },
      { "email": "jane@example.com" }
    ]
  }'
```

The response is `200` with a `results` array aligned by index with `candidates`. Branch on each result's `ok`. `status` is `found` or `not_found`.

<Warning>
  A found item carries a `candidates` **array**, not one candidate. An identity reference resolves to every membership row your agency holds for that person. A candidate owned by two of your recruiters has two independently editable preference rows, each with its own `candidateUserId`. Read them all, or pick one by `candidateUserId`.
</Warning>

* Entries are ordered by ParaAI eligibility expiry, latest first. Rows with no expiry come last.
* Values are never rejected. An unparseable `linkedinUrl`, an unknown `email`, or an item that names no candidate answers `not_found` at its own index, so one bad CRM row doesn't cost you the other 49.
* Only schema violations (a field with the wrong JSON type, or an empty `candidateUserId`) fail the whole request with `400`.
* Candidates added before the current validation rules may read back `null` numbers or empty arrays.

There's no batch write. Use the per-candidate `PUT` below with the `candidateUserId` this endpoint returned.

## Replace

[`PUT /talent-network/candidates/{candidateUserId}/preferences`](/agency/api-reference/replace-talent-network-candidate-preferences) is a **full replace**. The body is the same object `submit` takes as `preferences`, sent flat:

```bash theme={null}
curl -X PUT "https://external-api.paraform.com/api/external/v1/talent-network/candidates/$CANDIDATE_USER_ID/preferences" \
  -H "Authorization: Bearer $PARAFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locations": ["new_york", "boston"],
    "workplaceTypes": ["REMOTE", "HYBRID"],
    "salaryMin": 170000,
    "salaryMax": 210000,
    "idealFundingRounds": ["SERIES_B", "SERIES_C"],
    "visaAuthorization": "HAS_VISA_AUTHORIZATION"
  }'
```

* Validation failures return `400` with per-field errors in `details.fieldErrors`.
* Omitted optional fields are **cleared**: numbers become `null` and arrays become empty. The write also overwrites values a recruiter set in the app. To keep existing values, read, modify, then write.
* `salaryMax` and `ote` also accept an explicit `null`, which means the same as omitting them. So the body a `GET` returns can be sent straight back.
* The response echoes the stored state and matches a later `GET`. ParaAI matching and the in-app preferences reflect it immediately.
* Retries converge, so no `Idempotency-Key` is needed.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.