# Get all member user fields with pagination

`GET /api/members/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 member user fields with pagination](https://support.gladstonesoftware.cloud/reference/members/get-all-member-user-fields-with-pagination)

## Overview

Bulk-exports the custom fields held against members. *User fields* are the extra data points an operator defines for themselves — a marketing source, a medical flag, a staff-only note — beyond the standard member profile. This endpoint walks every member, rather than one member at a time.

It has **two mutually exclusive modes**, and choosing the wrong one is the most common failure:

- **Delta mode** (the default) — returns members whose user fields were modified between `dateFrom` and `dateTo`. If you supply neither, the window silently defaults to *the last day only*, so an unparameterised call is not a full export and will look empty on a quiet tenant.
- **Full export mode** — set `returnAllRecords=true` and omit both dates. Supplying either date alongside it is rejected with `400`.

Paging is by continuation token, and **tokens are mode-specific**: a token minted in full-export mode cannot be replayed in delta mode, and vice versa. Keep `returnAllRecords` identical for every call in a sequence.

Ending is signalled by an empty `continuation-token` — an empty string, not `null` and not an absent field. Stop when you see it, not when `profiles` is short.

One shape quirk to plan for: the response is a single object with `profiles` and `continuation-token` on it, not an array, even though the field paths below are written with a leading `[]`.

## Endpoint

`GET {base_url}/api/members/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 |
|------|------|----------|-------------|
| `dateFrom` | string (date-time) | No | The start of the modification window, in ISO 8601 (`2026-08-19` or `2026-08-19T10:30:00Z`); other formats such as `19/08/2026` are rejected with `400`. Must be earlier than `dateTo`. **Defaults to the start of yesterday when omitted** — it does not mean from the beginning of time, which is why a call with no parameters returns almost nothing. Cannot be combined with `returnAllRecords=true`. |
| `dateTo` | string (date-time) | No | The end of the modification window, in the same ISO 8601 format. Defaults to now when omitted. It cannot be in the future — even by a second — so do not pad it forward to catch late writes. Must be later than `dateFrom`. Cannot be combined with `returnAllRecords=true`. |
| `limit` | integer (int32) | No | How many member profiles to return per page. Valid range is 1 to 100. Anything above 100 is rejected with `400`; `0` and negatives are **not** an error but are silently replaced by the default of 25. Keep it consistent across a paging sequence. |
| `continuation-token` | string | No | The cursor for the next page, taken verbatim from the `continuation-token` in the previous response. It is a readable composite of the last member id and last modified date, but do not construct one yourself: a malformed token is rejected with `400` naming the offending element or value. A token generated in full-export mode carries no usable date and is rejected if replayed with `returnAllRecords=false`. Omit it for the first page. |
| `returnAllRecords` | boolean | No | Switches from delta mode to a full export of every member. Defaults to `false`. Mutually exclusive with `dateFrom` and `dateTo` — sending it as `true` alongside either is rejected with `400`. It also changes what continuation tokens mean, so it must stay the same for every call in a paging sequence. |
| `user-field-ids` | string | No | Restricts the `userFields` array on each profile to particular fields, as a comma-separated list of numeric field ids — for example `4,11`. Every element must parse as an integer, or the whole call fails with `400` naming the offending value. It filters the fields shown, not which members are returned. Omit it to get every user field configured for the tenant. |

## Sample request

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

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `[].profiles` | array of object | One page of members, at most `limit` long, each with their user field values. Empty when nothing matched the window or when you have paged past the end — an empty page always comes with an empty `continuation-token`. |
| `[].profiles[].memberId` | integer (int32) | The member these fields belong to. This is the ordinary member id you use elsewhere in the Members API, so it is your join key back to member profiles. |
| `[].profiles[].userFields` | array of object | The member's user field values. Only fields that have a value stored for this member appear — a field the operator has configured but this member has never had set is simply absent, so do not treat the array as a complete schema. Narrowed further if you sent `user-field-ids`. |
| `[].profiles[].userFields[].userFieldId` | integer (int32) | The user field's id, as configured by the operator. This is the value to pass in `user-field-ids`, and it is stable within a tenant but different between tenants — resolve fields by id from the configuration endpoint rather than by description. |
| `[].profiles[].userFields[].userFieldDescription` | string | The operator's label for the field, such as `How did you hear about us?`. Display text, changeable by the operator, so do not key your logic on it. |
| `[].profiles[].userFields[].value` | string | The member's value, always returned as a string whatever the field's type. Parse it according to `type` — a `Date` field is an ISO date in a string, a `Numeric` or `Currency` field is a number in a string. For an `Options` field the value is the option's description text, not its id, and it is case-sensitive. `null` is possible only for the string-based types; a typed field with no value is absent rather than `null`. |
| `[].profiles[].userFields[].type` | string (enum) | How to interpret `value`: `Free_Format` free text, `Options` one of a configured list, `Date`, `Numeric`, `Currency`, `Hyperlink` a URL, `User` a staff user reference, `Member` a member reference. Note `Hyperlink` is spelled with a lower-case `l` on the wire. Currency fields carry no symbol here — the tenant's currency is a separate configuration value. |
| `[].profiles[].modifiedOn` | string (date-time) | When this member's user fields were last changed. This is the field `dateFrom` and `dateTo` filter on and the field paging orders by, so it is what you store as your high-water mark for the next delta run. `null` is possible for records that have never been modified since creation. |
| `[].profiles[].createdOn` | string (date-time) | When this member's user field record was first created. Not used by the date filter — filtering is on `modifiedOn` only — so do not expect `dateFrom` and `dateTo` to bound this value. |
| `[].continuation-token` | string | The cursor to pass back as `continuation-token` for the next page. An **empty string** when there are no further records; that, not a short page, is the end-of-data signal. It encodes the last member id and last `modifiedOn` in the page, and is only valid for the same `returnAllRecords` mode that produced it. |

## Sample response

```json title="200 OK"
[
  {
    "profiles": [
      {
        "memberId": 12345,
        "userFields": [
          {
            "userFieldId": 12345,
            "userFieldDescription": "string",
            "value": "string",
            "type": "Free_Format"
          }
        ],
        "modifiedOn": "2026-08-14T19:00:00Z",
        "createdOn": "2026-08-14T19:00:00Z"
      }
    ],
    "continuation-token": "string"
  }
]
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | `returnAllRecords=true` was sent together with `dateFrom` and/or `dateTo`. The body is `You cannot specify a dateFrom and/or dateTo if returnAllRecords is set to true`. |
| `400` | Bad request | `dateTo` is in the future. The body is `DateTo {dateTo} cannot be in the future`. |
| `400` | Bad request | `dateFrom` is later than `dateTo`. The body is `DateFrom {dateFrom} must be before dateTo {dateTo}`. Because `dateTo` defaults to now, a future `dateFrom` on its own triggers this too. |
| `400` | Bad request | `dateFrom` or `dateTo` is not a recognisable ISO 8601 date. The body is a validation error such as `The value '22-1-1' is not valid.` |
| `400` | Bad request | `limit` is greater than 100. The body is `Invalid value supplied for number of profiles per page. Limit must be an integer from 1 to 100.` A limit of `0` or below is not an error — it falls back to 25. |
| `400` | Bad request | A continuation token from full-export mode was replayed with `returnAllRecords=false`. The body is `The continuation-token {token} is not valid while returnAllRecords is false`. |
| `400` | Bad request | The continuation token is malformed. The body names the problem — an invalid element when a part is not a key and value pair, or an invalid value when a part will not parse. |
| `400` | Bad request | An entry in `user-field-ids` is not a whole number. The body is `One or more of the user fields ({value}) in the filter is invalid`. |
| `500` | Internal server error | An unexpected server-side failure while reading the user fields. |
