# Get all user field configurations

`GET /api/configuration/user-fields`

- **Success:** `200 OK`
- **Products:** Operator (`FOH`), Consumer (`CONSUMER`) (any one of these products, not all)
- **Features:** Online Bookings (`ONLINEBOOK`), Online Joining (`ONLINEJOIN`), Front of House Generic Access (`FOHGENERIC`) (any one of these features, not all)

HTML version: [Get all user field configurations](https://support.gladstonesoftware.cloud/reference/configuration/get-all-user-field-configurations)

## Overview

Lists the custom member fields ("user fields") an operator has defined, together with their type, default value, option list and localised labels. Read this before writing to or reading from a member's custom fields: it tells you which fields exist, what shape each value must take, and which options are acceptable.

The response is a JSON array. With no query string you get every user field the tenant has defined; with `ids` you get exactly the fields you asked for, or a `404` if any of them is missing.

Four things about this response cost integrators the most time.

**Inactive fields are included.** Each field has an `isActive` flag, this endpoint does not filter on it, and retired fields therefore appear alongside live ones. If you are rendering a form or synchronising a schema, filter on `isActive` yourself.

**`options` is omitted entirely unless `type` is `Options`.** It is not `null` and not an empty array for other types — the property is absent from the JSON. Code that reads it unconditionally will fail.

**`defaultValue` is polymorphic.** Its JSON type depends on `type`: a string, a number, or an object. See the field description below before parsing it.

**Nothing is sorted.** No ordering is applied, so neither the fields nor their options arrive in `sortOrder` sequence. Sort client-side.

When you later write a member's value for a field of type `Options`, you send the option's `description` text — matched case-sensitively — not its `id`.

A field created or amended through [Create user field configuration](create-user-field-configuration.md) or [Update user field configuration](update-user-field-configuration.md) may not be visible here immediately.

A tenant with no user fields defined returns `200` with an empty array, not `404`.

## Endpoint

`GET {base_url}/api/configuration/user-fields`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `ids` | string | No | A comma-separated list of user field ids to return, matched against each field's `id` — for example `ids=1,4,9`. Omit it to return every user field. Every segment must parse as a signed 16-bit integer (−32768 to 32767) or the whole request fails with `400`, so `ids=` with no value is rejected: the empty string is treated as a segment. Filtering is all-or-nothing rather than best-effort — if every segment parses but any id matches no field, you get `404` for the request, not a shorter list. |

## Sample request

```bash title="cURL"
curl -X GET '{base_url}/api/configuration/user-fields' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true'
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `[].id` | integer (int32) | Identifies the field when reading or writing a member's value for it, and is what you pass in the `ids` query parameter. A 16-bit integer despite the type shown here, assigned as one more than the highest existing id when the field is created — so it is tenant-local and tells you nothing about the field's meaning. Do not hardcode it across tenants or environments. |
| `[].description` | string | The operator's label for the field, as staff see it. At most 25 characters, which is short enough that operators routinely abbreviate. It can come back `null`, and it is not unique. It is the untranslated label; localised versions, where configured, are in `translations`. |
| `[].sortOrder` | integer (int32) | The position the operator wants this field to occupy when the set is displayed as a form. Nullable, so it can be `null`, and the response is **not** returned in this order — sort on it yourself and decide where `null` goes. A new field is given one more than the current highest value, so values are usually but not necessarily contiguous or unique. |
| `[].type` | string (enum) | The kind of value the field holds, which determines how a member's value is validated, what shape `defaultValue` takes, and whether `options` is present at all. One of `Free_Format` (free text), `Options` (a choice from `options`), `Date`, `Numeric`, `Currency`, `Hyperlink`, `User` or `Member`. Returned as a string, not a number. Note the casing of `Hyperlink`. Only `Free_Format`, `Options`, `Hyperlink` and `User` accept a `null` member value; to clear a `Date`, `Numeric`, `Currency` or `Member` field you delete the member's value instead. |
| `[].defaultValue` | string | The value pre-filled for a new member, and **not always a string** despite the type shown here. `null` when the field has no default configured, and also `null` when the default could not be read as the field's type — no error is returned, so a `null` here does not prove the operator left it blank. For `Free_Format`, `Options` and `Hyperlink` it is a string; for `Numeric` and `Member` a JSON integer; for `Currency` a JSON decimal, derived by stripping every character that is not a digit or a full stop, so a default of `£12.50` arrives as `12.50`; for `Date` an object with `dayType`, `monthType` (each `Absolute` or `Relative`), `day`, `month` and `year`; for `User` an object with `isCurrentUser` and `userId`, where `isCurrentUser` true means the default is whoever is signed in. The default is capped at 255 characters. |
| `[].locked` | boolean | Whether at least one member already has a value recorded against this field. It is worked out on each request, not a flag an operator sets. While it is `true`, [Update user field configuration](update-user-field-configuration.md) rejects any change to `type` or to the option list with a `400`; `description`, `sortOrder`, `isActive` and the default value can still be changed. Use it to decide which controls to disable in a configuration screen. |
| `[].isActive` | boolean | Whether the operator still considers the field in use. **This endpoint does not filter on it** — retired fields come back with `false` and are otherwise indistinguishable, so exclude them yourself before rendering a form or building a schema. Defaults to `true` for newly created fields, and is never `null`. |
| `[].options` | array of object | The permitted choices, present **only** when `type` is `Options`. For every other type the property is absent from the JSON altogether — not `null`, not `[]` — so read it defensively. No ordering is applied. Options are also what makes a field locked in practice: once a member has selected one, the list can no longer be changed. |
| `[].options[].id` | integer (int32) | Identifies the option **within its parent user field only** — it is unique only together with the user field id, so the same number appears against different fields and means different things. Never treat it as a global option identifier, and note that it is not what you send when setting a member's value. |
| `[].options[].description` | string | The option's label, and the value you must send when writing a member's value for this field — matching is on this exact text, case-sensitively, not on `id`. At most 50 characters and nullable, so an option with a `null` description cannot be selected through the API. Untranslated; localised labels are in `translations`. |
| `[].options[].translations` | array of object | Localised replacements for this option's label. An empty array when the operator has configured none, which is the common case. Translations are held under the `UserFieldOption` translation type, identified by the parent field id and the option id together. |
| `[].options[].translations[].type` | string | Despite the name, this is **not** a user field type and does not take the values of `[].type`. It is the name of the property on the option that this text replaces — in practice `Description`. It is not restricted to the user field type values. |
| `[].options[].translations[].culture` | string | The culture this text applies to, as a code from the tenant's configured culture list — see [Get all available cultures](get-all-available-cultures.md). At most 20 characters and constrained to that list, so an arbitrary BCP 47 tag is not necessarily valid. |
| `[].options[].translations[].text` | string | The localised text to display instead of `[].options[].description` for the given culture. It replaces the label only — you still send the untranslated `description` when writing a member's value. |
| `[].translations` | array of object | Localised replacements for this field's own label. Always present, as an empty array when none are configured. Held under the `UserField` translation type, identified by the field's `id`. |
| `[].translations[].type` | string | Despite the name, this is **not** a user field type and does not take the values of `[].type`. It is the name of the property on the user field that this text replaces — in practice `Description`. |
| `[].translations[].culture` | string | The culture this text applies to, as a code from the tenant's configured culture list — see [Get all available cultures](get-all-available-cultures.md). At most 20 characters and constrained to that list. |
| `[].translations[].text` | string | The localised text to display instead of `[].description` for the given culture. Display only; it has no effect on how values are written or validated. |

## Sample response

```json title="200 OK"
[
  {
    "id": 1,
    "description": "T-shirt size",
    "sortOrder": 1,
    "type": "Options",
    "defaultValue": "Medium",
    "locked": true,
    "isActive": true,
    "options": [
      { "id": 1, "description": "Small", "translations": [] },
      { "id": 2, "description": "Medium", "translations": [] },
      { "id": 3, "description": "Large", "translations": [] }
    ],
    "translations": []
  },
  {
    "id": 2,
    "description": "Induction date",
    "sortOrder": 2,
    "type": "Date",
    "defaultValue": {
      "dayType": "Relative",
      "monthType": "Relative",
      "day": 0,
      "month": 0,
      "year": 0
    },
    "locked": false,
    "isActive": false,
    "translations": []
  }
]
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | A segment of `ids` is not a signed 16-bit integer. The body is the plain-text message `ID {value} is not valid.` naming the first offending segment. `ids=` with an empty value triggers this, because the empty string counts as a segment — omit the parameter entirely instead. |
| `404` | Not found | You supplied `ids` and at least one of them matches no user field. The body is the plain-text message `One or more user field IDs were not found.`, which does not say which one. The whole request fails; you never receive a partial list. Requesting no ids at all never produces a `404` — an empty configuration returns `200` with `[]`. |
| `500` | Internal server error | Any unexpected server-side failure while reading the configuration. The response has **no body**, so nothing distinguishes one cause from another; note the time and escalate. |
