> For the complete documentation index, see [llms.txt](https://help.security.gallagher.com/knowledge-hub/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.security.gallagher.com/knowledge-hub/developer/rest-api/cardholders.md).

# Cardholders

A cardholder is a user account.

These methods give you read and write access to the cardholder data in the Command Centre database. You can download cardholders, create them, search them by name or Personal Data Field (PDF) value, and work on them as you would in the interactive clients.

The first use case below introduces the main entry point. It is a paginated search interface that gives you basic data for any number of cardholders. You will not receive many fields by default, but you can ask for more using the `fields` query parameter.

A field called `href` serves both as a unique identifier for the cardholder and the location of what we call his or her *details* page. You can submit an HTTP GET to that href to retrieve the entire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it. Be careful: there is no coming back from deleting a cardholder.

### Efficiency tips

* When searching for a cardholder by personal data or any object by name, if you have the exact string, put it in quotes `"..."`. It is still a case-insensitive search but it attempts to match the entire string. Without the quotes Command Centre will perform a substring search, which is slower and may return more results than you need. In one test, a substring search of half a million cardholders took more than ten seconds, but with quotes it took a tenth of a second. That is a 100x speedup. If the server believes you have omitted the quotes in error, it will include a polite reminder in its results.
* When downloading a significant number of items, sort by ID.
* Set your page size as large as your client can handle. The default was 100 before 8.70; using the `top` parameter to take it to 1000 or more gives *much* better throughput.

To give you an idea of the effect of setting `sort` and `top`, here are some timings for a download of 200,000 cardholders from CC 8.40:

| Sorting           | Pagination    | Time        |
| ----------------- | ------------- | ----------- |
| Default (by name) | Default (100) | 28 minutes  |
| `sort=id`         | Default (100) | 5 minutes   |
| Default (by name) | `top=1000`    | 3.5 minutes |
| `sort=id`         | `top=1000`    | 1 minute    |

* In version 8.00 or later, use the `fields` parameter to add fields to the results of a search, saving you GETting the details page.

  For example, to see the names, personal data fields, and access groups of all your cardholders:

  `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`

  It adds to the payload size, though, so only do this if you are certain that the search will not return too many cardholders you are not interested in.
* If you are writing an integration that synchronises users from a source of truth to Command Centre, see the 'Synchronising a user directory' use case.
* If you must get an object's details page, and you are on v8.00 or later, using the `fields` parameter will still save work. Be explicit with what you want: do not use `fields=defaults` unless you need every field that 'defaults' gives you and you accept that we may add fields to the defaults in future.
* When creating cardholders, assign their cards, groups, and other attributes in the POST. This is significantly quicker than sending a handful of PATCHes chasing after your POST, and if you are interrupted, you will not end up with a partially-configured cardholder.
* When modifying a cardholder, do all your operations in one PATCH. This is much quicker than doing it in several smaller PATCHes.

### Write-locked cardholders

If an operator has unsaved changes on a cardholder object when your integration attempts to update it, your PATCH will fail with a 409 error. The message will tell you the operator and workstation that is holding the lock; we suggest your application logs that and tries its update again later.

### Licensing

All of the API calls described here are available with the RESTCardholders licence. Licences that enable additional features are described in each route.

### Cardholder use cases

#### Searching for cardholders by name

1. `GET /api`.
2. Follow the link at `features.cardholders.cardholders.href` ↪, appending a search term to narrow the results and the `fields` parameter to add the fields you need.
3. Process the results, following the `next` link until there isn't one.

#### Downloading all cardholders

1. `GET /api`
2. Follow the link at `features.cardholders.cardholders.href` ↪. This is effectively a cardholder search with no filters. You should add `sort` and `top` query parameters to sort by ID and increase the number of cardholders per page: see the efficiency tips!
3. Process the bundle of cardholders in the result.
4. Follow the link at `next.href` if there is one, and repeat.

#### Synchronising a user directory

This is a common use case, described in the section on Cardholder changes. It shows how to:

1. Get a bookmark at the head of the queue of cardholder changes.
2. Do a one-off sync of all cardholders.
3. Loop to stay up to date, starting with the bookmark.

That is how Gallagher's cardholder-synchronising integrations work.

#### Searching for cardholders by Personal Data Field value

Say you want to find a cardholder with a particular employee ID, and Command Centre is holding that in a personal data field called "employee\_ID".

First, reconsider, and look at the 'Synchronising a user directory' use case above. If you are going to be doing this for many cardholders, GETting cardholders in bulk and filtering out the interesting ones client-side is more efficient than a large number of unique PDF searches. You don't have to get all cardholders; you could get all cardholders in one division, or all cardholders with a non-blank value for your PDF (shown below), or a combination. It will be quicker than a sequence of searches that return one cardholder each.

Before you can start a cardholder search, you must find the PDF's identifier.

1. `GET /api`.
2. v8.10 or later: follow the link at `features.personalDataFields.personalDataFields.href` ↪, appending the query `name="employee_id"`. This will return every PDF in the system with that name. There will be more than one, in rare cases.
3. Earlier versions: follow the link at `features.items.items.href` ↪, appending the query `name="employee_id"` (after a `?` or `&`, of course). This will return every item in the system with that name. Pick the one with a type name of 'Personal Data Field'. Or you could append `type=33` to the item search: you will only be shown PDFs.
4. Note the ID of the item. It will be a short alphanumeric.

Case does not matter when searching by name but remember the quotes: Command Centre will perform a substring match if you omit them, which is vastly slower and (for the example above) return you the PDFs 'previous\_employee\_ID' and 'employee\_ID\_allocated\_flag', if you have PDFs with those names.

Now you can use that PDF ID in a cardholder search.

1. Recall the JSON object you received from `GET /api`.
2. Follow the link at `features.cardholders.cardholders.href` ↪, adding a query separator and `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.

The results will only include cardholders who have *\<your\_pdf\_value>* as a substring of the Personal Data Field with ID *\<your\_pdf\_id>*. Again, the search is case-insensitive and you should bookend *\<your\_pdf\_value>* with quotes if you want to anchor it at each end. In our example we would `GET /api/cardholders?pdf_345="EID8888"`.

Use a percent sign `%` for *\<your\_pdf\_value>* if you want to see all cardholders who have a non-blank value for that PDF.

The server will reply with a default set of fields for your cardholder. If they are not what you'd like, you can use the `fields` query parameter to change them.

See the cardholder GET for more.

#### Creating a cardholder

1. `GET /api`.
2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups you wish to add your new cardholder to.
3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and cards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the `features` section of `/api`. In v8.10, the card types that your operator has the privilege to assign are at a new URL given in the field `features.cardTypes.assign.href` ↪.
4. Compose a JSON body using this example and this detail, then send it in a POST to the href at `features.cardholders.cardholders.href` ↪ on the `/api` page.

#### Modifying a cardholder

1. Find your cardholder using one of the processes above.
2. If you need the current values before you update them, use the `fields` parameter to add fields (such as accessGroups, cards, or competencies) to the search results.
3. PATCH the cardholder's href with a document describing the additions, deletions, and modifications you wish to make to the cardholder and his or her associations.

In its simplest form, your document could be a collection of key/value pairs much the same as you receive from a GET to the same href. More complex forms allow adding, modifying, and removing cards, competencies, group membership, PDFs, and relationships. Three examples follow.

#### Removing a cardholder from groups

1. Find your cardholder using one of the processes above.
2. Follow the link to their details page or add `fields=accessGroups` to the search. That will show all their access group memberships.
3. PATCH the cardholder with the hrefs of the memberships you want to delete in an array called `remove` inside a block called `accessGroups`.

The access groups section describes what to do when you want to start with a group and remove several cardholders from it.

#### Removing all group memberships, competencies, relationships, cards, or lockers

This shows how you could remove a cardholder from all his or her access groups, but you could just as simply apply the process to other fields that come back as arrays, such as competencies, relationships, operator groups, cards, and lockers.

1. GET your cardholder's details page, or search with `accessGroups` in the field list. Either way, you will receive all their access group memberships.
2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called `accessGroups` at the root level of a JSON object.
3. PATCH the cardholder with that JSON object. That will remove the cardholder from all the groups that came back in the GET.

#### Assigning a new card to a cardholder

1. `GET /api`.
2. Follow the link at `features.cardTypes.cardTypes.href`↪ to find the href for the new card's type.
3. If you want to create a card in a state other than the default, get it from the same page.
4. Find the href for the cardholder using one of the search methods above.
5. Optional: follow the link at `edit.href` on the cardholder page. If there is a link at `update.href` of the results, your REST client has permission to modify the cardholder.
6. PATCH the cardholder's href according to the cardholder patch schema.

#### Deleting a cardholder's PDF value

1. Find the URL of your cardholder using one of the processes above.
2. PATCH your cardholder with this in the body: `{ "@name_of_PDF": null }`

#### Deleting a cardholder

1. Find your cardholder using one of the processes above.
2. Send an HTTP DELETE to the cardholder's href.

#### Updating a cardholder's location

1. Find the href of your target access zone using the link at `features.cardholders.updateLocationAccessZones` ↪ in the results of `GET /api`.
2. Find your cardholder using one of the processes above. If you are using search, add `updateLocation` to the `fields` parameter to save you having to GET their details page later.
3. Send an HTTP POST to that cardholder's `updateLocation.href` link.

#### Finding which access zones and doors a cardholder can access

1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field list. Either way, you will receive all their access group memberships.
2. Iterate through all the access groups looking into their `access` blocks for access zones and schedules. Recurse up the access group hierarchy by following each group's `parent.href`.

Now you have the cardholder's access zones. If you want doors:

3. For each access zone, GET its href and look in its `doors` block.

#### Finding recently-created cardholders

Informing you when Command Centre creates cardholders is a function of the Cardholder changes call, described there.

However for that to work you need to make API calls before and after the cardholders are created. If they already exist, a workaround is to get a page of cardholders sorted by database ID *descending* using `sort=-id&top=10`. Change the ten to the number of cardholders you'd like, obviously. Because Command Centre allocates database IDs to new items in increasing order, the first page you get will be the most-recently created cardholders. This is not suitable for use in a production system! Use the cardholder changes call instead.

### Field names in query parameters

The cardholder search and details operations' `fields` parameter and the change tracking operation's `fields` and `filter` parameters take a list of field names. Other sections of this document describe how those parameters affect the API calls, but their format is the same so this section will cover it once.

You can form the name of a field by joining the components of its JSON path with dots. Fields at the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF), have just one component. Fields that are one level down, such as `accessGroups.status`, have two.

Treat the string matches as case sensitive: use `lastName` rather than `lastname`.

The string must not contain any spaces. Just alphanumerics, underscores, commas, and dots.

To serve as examples, this is the list you can choose from in 8.30:

`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`, `lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`, `personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`, `relationships`, `lockers`, `cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`, `cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`, `cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`, `cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`, `cards.pivData.chuid.orgIdentifier`, `cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`, `cards.invitation.mobile`, `notifications.enabled`, `notifications.from`, `notifications.until`, `accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`, `accessGroups.until`, `accessGroups.status`, `relationships.href`, `relationships.cardholder`, `relationships.role`, `lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`

Later versions of Command Centre added many more. See the details operation for the complete list of cardholder fields.

A special value `default` applies a default set of fields that varies with the call you're making.

`personalDataFields` is also special. It will give you the personalDataDefinitions block plus all the PDF values at the root level with their names preceded by '@'-signs.

### Licensing

All of the API calls described here are available with the RESTCardholders licence, with the following exceptions and notes:

* Visitor management calls need both RESTCardholders and VisitorManagement.
* Creating, modifying, and deleting cardholders' connections to competencies requires the RESTCardholders licence.
* Managing access group memberships is enabled by the RESTCardholders licence. Managing access group items, card types, personal data definitions, and roles is coming soon.
* Lockers and locker banks are also available with the RESTStatus licence, and a subset of their fields (enough to allow overriding them) are available with the RESTOverrides licence.

The server will return a 403 if you attempt an operation for which the server is not licensed.

## Search cardholders

> This call returns cardholders matching your search criteria.\
> \
> The result will contain no more than 100 or 1000 cardholders depending on your version; you\
> should follow the \`next\` link, if it is present, to collect the next batch.\
> \
> When you have loaded all the cardholders there will be no \`next\` link.\
> \
> If your result set is empty it means your operator does not have the privilege to view any\
> cardholders.  Perhaps there are none in the divisions in which your operator has privileges,\
> or your operator has no privileges at all.\
> \
> Adding or modifying cardholders between calls to this API will not affect the pagination of\
> its results if you sort by ID.\
> \
> \[Do not code this URL]\(forward-compatibility-hateoas) into your application.  Take it from the \`href\` field in the\
> \`features.cardholders.cardholders\` section of \`/api\`.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"parameters":{"sort":{"name":"sort","in":"query","schema":{"type":"string","enum":["id","name","-id","-name"]},"required":false,"description":"Changes the sort field between database ID and name.\n\nIf you prefix `id` or `name` with a minus sign (ASCII 45), the sort order is reversed.\n\nThere are two very strong reasons to sort by ID:\n\n1. Sorting by name carries a risk of missing or duplicating objects if your result set spans\n   multiple pages and another operator is editing the database while your REST client is\n   enumerating them.  This is known as \"page drift.\"  Sorting by ID does not carry that risk.\n2. Following a `next` link is _dramatically_ quicker when sorting by ID.\n\nWe _strongly_ recommend sorting by ID.  In case you were still in doubt, we will do that by\ndefault in a future version of Command Centre.\n\nThe server silently ignores anything except the options listed here.\n"},"division":{"name":"division","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"description":"Limits the returned items to those that are in these divisions.\n\nThat includes all the items in those divisions' child divisions, because Command Centre treats\nitems as though they are also in their division's parent, and its parent, and so on up to the\nroot division.\n\nSeparate the IDs of the divisions in you are interested with commas.  Item IDs are short\nalphanumeric strings, not URLs.\n\nResults are undefined if you provide an ID that is not in the form of a division ID.\n\nSearch parameters are ANDed together.\n"},"directDivision":{"name":"directDivision","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"description":"Restricts items to those whose division is in this list.\n\nUnlike `division=`, it does not follow ancestry.  That means it will limit the search to items\nthat are directly assigned to the divisions you list, whereas `division` (without the\n`direct`) will also include items that are assigned to their descendants.\n\nList the IDs of the divisions you are interested in separated by commas.  Item IDs are short\nalphanumeric strings, not URLs.\n\nBecause this is the first query parameter we added that contains a capital letter, this will\nbe the first time you have had to think about case sensitivity.  `directDivision` works,\n`directdivision` does not.\n\nResults are undefined if you provide an ID that is not in the form of a division ID.\n\nSearch parameters are ANDed together.\n\nAdded in 9.20.\n"},"description":{"name":"description","in":"query","required":false,"schema":{"type":"string"},"description":"Limits the returned items to those with a description that matches this string.  By default it\nis a substring match; surround it with double quotes `\"...\"` for an exact match.  A `_` will\nmatch any single character, and a `%` will match any substring.  With or without quotes,\nhaving either of these wildcards in the string will anchor it at both ends as though you had\nsurrounded it with `\"`.\n\nThe search is always case-insensitive.  Results are undefined if you search for the empty\nstring (`description=` or `description=\"\"`).\n\nSearch parameters are ANDed together.\n"},"pos":{"name":"pos","in":"query","required":false,"schema":{"type":"integer"},"description":"Reserved for internal use.  You may see it in URLs you receive from the server, but you must never add it yourself."},"skip":{"name":"skip","in":"query","required":false,"schema":{"type":"integer"},"description":"Reserved for internal use.  Do not add it to your queries."}},"schemas":{"CardholderSearch":{"title":"Cardholder search","type":"object","properties":{"results":{"type":"array","description":"An array of cardholder summaries.","items":{"$ref":"#/components/schemas/CardholderSummary"}},"next":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}},"description":"The link to the next page.  Absent if you have retrieved them all."}},"description":"An array of cardholder summaries, and a `next` link for more."},"CardholderSummary":{"title":"Cardholder summary","type":"object","properties":{"href":{"type":"string","format":"uri-reference","readOnly":true,"description":"A link to a [cardholder detail](models#cardholderdetail) object for this cardholder.\nThis is Command Centre's identifier for this cardholder:  use it whenever you need to\nspecify a cardholder in REST operations.\n"},"id":{"type":"string","description":"An alphanumeric identifier, unique to the server.  No API calls use cardholder IDs.\n\nDeprecated.  Use the href instead.\n","readOnly":true},"firstName":{"type":"string"},"lastName":{"type":"string"},"shortName":{"maxLength":16,"type":"string","description":"If you supply a string that is too long when creating or updating\na cardholder, Command Centre will truncate it.\n"},"description":{"type":"string"},"authorised":{"type":"boolean","description":"This is called 'Cardholder Authorised' in the administrative clients.  If false, Command\nCentre will deny card access decisions for this cardholder.\n\n`authorised` is false by default.  If you want a new cardholder to open doors, be sure to\nset it to true in your POST.\n\nYou need the 'Edit cardholders' privilege to set this true.  In versions prior to 8.80\nyour operator also needed that privilege to set it false, but in 8.80 and later\n'De-authorise cardholder' is enough on its own.\n"}},"description":"The cardholder search at `/api/cardholders` returns an array of these.  It is a subset of what\nyou get from a cardholder's detail page at `/api/cardholders/{id}` (linked as the href in this\nobject), to be more suitable for large result sets.\n"}}},"paths":{"/api/cardholders":{"get":{"tags":["Cardholders"],"summary":"Search cardholders","description":"This call returns cardholders matching your search criteria.\n\nThe result will contain no more than 100 or 1000 cardholders depending on your version; you\nshould follow the `next` link, if it is present, to collect the next batch.\n\nWhen you have loaded all the cardholders there will be no `next` link.\n\nIf your result set is empty it means your operator does not have the privilege to view any\ncardholders.  Perhaps there are none in the divisions in which your operator has privileges,\nor your operator has no privileges at all.\n\nAdding or modifying cardholders between calls to this API will not affect the pagination of\nits results if you sort by ID.\n\n[Do not code this URL](forward-compatibility-hateoas) into your application.  Take it from the `href` field in the\n`features.cardholders.cardholders` section of `/api`.\n","parameters":[{"$ref":"#/components/parameters/sort"},{"name":"top","in":"query","description":"Sets the maximum number of cardholders to return per page.\n\nOlder versions of Command Centre returned 100.  That is acceptable for a GUI application\nthat will only display the first page of cardholders, but for integrations that intend to\nproceed through the entire database it causes a lot of chatter.\n\nVersion 8.70 will return 1000 items per request by default.  1000 is about where a graph\nof performance versus page size begins to level out.  You may see some improvement by\ntaking it even higher.\n","schema":{"minimum":1,"type":"integer"}},{"name":"name","in":"query","description":"Limits the results to cardholders with a name that matches this string.  By default, it is\na substring search against the first name or the last name or the concatenation\n'lastName, firstName'; surround the parameter with double quotes `\"...\"` for an\nexact search.\n\nWithout quotes, a percent sign `%` inside your search string will anchor the search at\nboth ends (so it will no longer be a substring search) and the `%` will match any\nsubstring.  For example, `boothroyd,%` will only match cardholders whose last name is\nBoothroyd.\n\nThe search is always case-insensitive.  Results are undefined if you do a substring search\nfor the empty string (`name=`).  You will receive no cardholders if you search for those\nwith no name (`name=\"\"`), as all items must have a name.\n\nBecause a plus sign `+` represents a space in a query string, replace each plus sign in\nyour search string with `%2B`.\n\nThe search parameters form a logical conjunction.  They are ANDed\ntogether.  Therefore if you search for `name=Mary&pdf_1315=nanny` you will only get back\ncardholders with 'Mary' in their name and 'nanny' in the PDF with ID 1315.\n","schema":{"type":"string"}},{"name":"pdf_{id}","in":"query","description":"Limits the results to cardholders with a value for the Personal Data Field with this ID\nthat matches the parameter.\n\nIt is a substring match by default; surround it with double quotes `\"...\"` for an exact\nmatch.  Tests showed an exact match to be 100x quicker than a substring search on a large\ndatabase.\n\nWithout quotes, `_` will match any single character and `%` will match any substring.\nHaving either in your term will anchor the string at both ends so it will not be a\nsubstring search.  A lone `%` will return any cardholder who has this PDF set to a\nnon-null value.\n\nSearching for a blank value using `pdf_xxx=\"\"` or `pdf_xxx=` currently matches no\ncardholders, which is not useful.  Do not rely on that behaviour since we will change it\nin a future version of Command Centre to return cardholders with no value for that PDF.\n\nBecause a plus sign `+` represents a space in a query string, turn plus signs in your\nstring into `%2B`.\n\nThe search is always case-insensitive.\n\nSearch parameters form a logical conjunction.  They are ANDed\ntogether.  Therefore the search `pdf_1315=nanny&pdf_1315=paratrooper` will only return\ncardholders whose PDF 1315 contains the strings 'nanny' and 'paratrooper'.\n","schema":{"type":"string"}},{"$ref":"#/components/parameters/division"},{"$ref":"#/components/parameters/directDivision"},{"$ref":"#/components/parameters/description"},{"name":"accessZone","in":"query","description":"Limits the results to cardholders who are in one of the access zones with the given IDs.\nDo not put quotes around the IDs, and separate them with commas.\n\nTo get everyone who is in _any_ access zone use `accessZone=*`.  It will return all\ncardholders who have badged at least once and who are not currently 'outside the system'.\nA cardholder is 'outside' if they badge through a door that has no access zone configured for that direction of travel,\nor if an operator manually moves them outside.\n","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"}}},{"name":"fields","in":"query","description":"Specifies the fields you want in the search results.  The values you can use here are the\nsame as you can for the [details page](cardholders#get-api-cardholders-id-).\nUsing it you can return everything on the search page that you would find on the details\npage with the exception of the `edit` and `updates` links.  Separate values with commas.\n\nUse the value `cards.encodingData` to receive links to the methods that return\ncard-encoding data.\n\nUse the special value `defaults` to return the fields you would have received had you not\ngiven the parameter at all.  Obviously only do that if you have more to add.\n\nUse the special value `personalDataFields` to return the `personalDataDefinitions` block\nas well as the PDF values at the root level of the cardholder object.\n\nUse the special value `pdf_XXX` (where `XXX` is the ID of a PDF definition) to include\njust that PDF.  If you do not have the PDF's ID, and don't mind the performance hit of\nretrieving all the PDFs, using `personalDataDefinitions` may be simpler.\n\nThere is a special value `defaults` which adds everything you would have received had you\nnot sent a `fields` parameter at all, but we advise against its use:  specifying every\nfield you want makes the response more predictable across different server versions.\n\nTreat the string matches as case sensitive:  use 'lastName' rather than 'lastname'.\n\nIn v8.00 you will receive the href and internal ID even if you do not ask for them.  In\n8.10 you will not.  If you are going to send the fields parameter and need the href or ID,\ninclude them.\n","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"},"default":["defaults"]}},{"$ref":"#/components/parameters/pos"},{"$ref":"#/components/parameters/skip"}],"responses":{"200":{"description":"Success.  See the note in the description about privileges if your result set is empty.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardholderSearch"}}}},"400":{"description":"The server could not make sense of your search terms."},"403":{"description":"The site does not have the RESTCardholders licence."}}}}}}
```

## Create a cardholder

> This creates a new cardholder, including his or her cards, group memberships, personal data,\
> competencies, and roles.\
> \
> \[Do not code this URL]\(forward-compatibility-hateoas) into your application.  Take\
> it from the \`href\` field in the \`features.cardholders.cardholders\` section of \`/api\`.\
> \
> The POST expects a document in the same format as the \[cardholder\
> detail]\(models#cardholderdetail).  Many fields are optional, of course, and some (like\
> the last successful access time) do not make sense when creating a cardholder.  See the\
> \[cardholder POST example]\(models#cardholderpost) for details.\
> \
> You will achieve better performance if you combine all you want to achieve into one POST,\
> rather than creating the cardholder bare with a POST then adding cards, groups, PDFs, etc.,\
> with PATCHes later.\
> \
> When successful it returns a location header containing the address of the new cardholder.\
> \
> Note that you can only create one cardholder per POST.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"schemas":{"CardholderPOST":{"title":"Cardholder POST","description":"This is part of the model of a POST body that creates a cardholder.  It does not contain\nevery field you can set when creating a cardholder, but shows the division (which is\nmandatory) and an access group, an access card, a supervisor, a competency, two personal\ndata fields, two lockers, and two elevator groups.\n\nThere are more fields on a cardholder than shown here.  For a complete list please see the\nschema for the detailed [cardholder object](models#cardholderdetail) that you receive\nfrom a cardholder href.\n","required":["division"],"properties":{"firstName":{"type":"string","description":"You must supply either this or the last name when creating a cardholder.\n\nThe maximum length of a cardholder's first name in version 9.50 is 50 characters.\n"},"lastName":{"type":"string","description":"You must supply either this or the first name when creating a cardholder.\n\nThe maximum length of a cardholder's last name in version 9.50 is 50 characters.\n"},"shortName":{"type":"string"},"description":{"type":"string"},"authorised":{"description":"Remember to set this true, as shown here in the POST, or later in a PATCH.  Otherwise your\nnew cardholder will never get through a door.\n","type":"boolean"},"division":{"type":"object","description":"Mandatory when creating any cardholder.  In this example, we want all students\nin division 5387.\n"},"@email":{"description":"An example PDF value.  In this example, access group 352 must include a PDF\ncalled 'email' otherwise this will appear to have no effect.\n","type":"string"},"@headshot":{"description":"An example image PDF encoded to Base64.  Access group 352 must include this PDF as well.\nAs a quick visual check on your encoding, JPEGs start with `/9j/`.\n","type":"string"},"personalDataDefinitions":{"description":"This is how you set and unset the notifications flags on PDFs.  If you do not do it here\nwhen you first give a cardholder a PDF, it will come from the PDF's definition.\n","type":"array","items":{"$ref":"#/components/schemas/CardholderPDF"}},"cards":{"description":"This example creates one physical card of type 600 with a PIN and a system-generated card\nnumber, and\nanother of type 654 which--judging by the invitation block--must be a mobile credential.\nCommand Centre will send an invitation to Nick at those coordinates.\n\nCard PINs were added to the API in 8.90.\n","type":"array","items":{"$ref":"#/components/schemas/CardholderCard"}},"accessGroups":{"type":"array","items":{"$ref":"#/components/schemas/CardholderAccessGroup"},"description":"Here you can add the access groups necessary to give your new cardholder the\nPDFs and access he or she needs.  In this example we set the activation date of\nthe access group membership to the first of January 2019.\n"},"operatorGroups":{"type":"array","items":{"$ref":"#/components/schemas/CardholderOperatorGroup"},"description":"Here you can add the operator groups necessary to give your new cardholder the\nsoftware access he or she needs.  Added in 8.50.\n"},"competencies":{"type":"array","items":{"$ref":"#/components/schemas/CardholderCompetency"},"description":"In this example we are giving our new cardholder a disabled competency, set to enable in\nJanuary 2019.\n"},"notes":{"type":"string"},"notifications":{"type":"object","description":"You can set or update any of the three fields in this block.","properties":{"enabled":{"type":"boolean"},"from":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"}}},"relationships":{"type":"array","items":{"$ref":"#/components/schemas/CardholderRelationship"},"description":"Here you would set the cardholders who will perform roles for this cardholder.\n\nThe example shows that this cardholder will have cardholder 5398 performing role 5396.\n\nRemember that you can supply an array of these objects if your cardholder is having more\nthan one role filled.\n"},"lockers":{"type":"array","description":"Here you set all the cardholder's locker assignments.  Ensure you are only attempting to\ngive your cardholder a locker according to site policy (one locker per locker bank, for\nexample), otherwise the POST will fail.\n\nThe example shows our cardholder receiving two lockers.\n","items":{"$ref":"#/components/schemas/CardholderLocker"}},"elevatorGroups":{"type":"array","description":"Here you set all the new cardholder's default elevator floors and passenger types. Passenger type properties\nare false by default.\n\nThe example shows our cardholder receiving a default floor for the first elevator group,\nwith the Code Blue feature enabled in the second group.\n\nAdded in 8.50.\n","items":{"$ref":"#/components/schemas/CardholderElevatorGroup"}}}},"CardholderPDF":{"title":"Cardholder PDF","description":"A personal data definition and its value for a cardholder.  Each definition is an object\ncontaining one property named after the PDF (plus a leading '@'), which in turn contains its\nvalue and notifications flag for the containing cardholder and an object containing some of\nthe PDF definition's basic fields.\n\nThis is returned as part of a [cardholder](models#cardholderdetail), and you can send it\nin a [POST](models#cardholderpost) or a\n[PATCH](models#cardholderpatch) to set the PDF's value and notification flag for\na cardholder.  The server will ignore the `definition` block if you send it back.  It uses the\nname of the block (minus its leading `@`) to find the PDF to change.\n\nWhile you can use this structure to change the value of a cardholder's PDF, it may be simpler\nto put a property in the root of the payload named after the PDF with a leading '@'.\n","type":"object","additionalProperties":{"$ref":"#/components/schemas/CardholderPDFContent"}},"CardholderPDFContent":{"title":"Cardholder PDF content","type":"object","properties":{"href":{"type":"string","description":"GET this link to receive this cardholder's value for this PDF.  If it is an image you will\nreceive the raw image data with an appropriate content-type.  A browser will render it\ncorrectly.\n\nIf the PDF is not an image the content type will be text/plain, encoded (like\neverything else in this API) as UTF-8.  But you should only use it for image PDFs\nbecause:\n\n* Values for scalar PDF types (everything except images) are also given to you in the\n  `value` field in this object, described below.\n\n* The format of date PDFs varies with the Command Centre version.  Old servers return\n  it in a format that depends on the server's locale.  This was a bug.  Newer servers\n  use ISO 8601.\n\n* If you learn this URL while a cardholder has the PDF, then the cardholder loses\n  it, then you GET the URL, results will vary depending on the server version.  The\n  correct result is a 404, and if the PDF is an image or the server is recent you\n  will see that.  But old servers may return a blank string or even a 500 for\n  non-image PDFs.\n\nThis field will be absent if the cardholder does not have a value for the PDF.\n\nIt is read-only:  do not send it in a PATCH or POST.\n","format":"uri-reference","readOnly":true},"definition":{"type":"object","properties":{"href":{"type":"string","description":"This is the href of the PDF's definition, common to all cardholders who hold a value\nfor this PDF.\n","format":"uri-reference"},"name":{"type":"string","description":"The PDF's name, without an '@' prefix.\n"},"id":{"type":"string","description":"Short alphanumeric identifier used elsewhere in the API to filter cardholder searches\nand add PDF values to alarm and event GETs.\n"},"type":{"type":"string","enum":["string","image","strEnum","numeric","date","address","phone","email","mobile"]}},"description":"The definition object is read-only:  do not send it in a PATCH or POST.\n","readOnly":true},"value":{"description":"In a GET response for text, numeric, and date PDFs this will be a scalar containing the\nvalue, but for image PDFs it will be an object containing a copy of the `href` field\nabove.  For date PDFs the string will comply with RFC 3339, but may or may not contain a\ntime depending on the version of Command Centre.  Newer versions omit the `T` separator\nand everything after it.  If there, it will be midnight UTC.\n\nIn versions up to and including 8.60, if a cardholder did not have an image PDF captured\n(in other words they were a member of an access group that included an image PDF, but they\ndid not have a value for it) the API returned the string 'Not captured' in the `value`\nfield.  Versions 8.70 and later will not return the `value` field at all if there is no image.\n\nIn a POST or PATCH send text PDFs (including dates) as strings, numeric PDFs as numbers or\nstrings that parse to numbers, and image data Base64-encoded into a string.\n","oneOf":[{"type":"integer","title":"Numeric","description":"The scalar value of the numeric PDF."},{"type":"string","title":"Text or date","description":"The scalar value of the text or date PDF."},{"type":"object","title":"Image","description":"An object containing an href which, when requested from the server, returns the\nimage PDF.  The server does not transcode images:  it will arrive as the same\ncontent type as uploaded.\n","properties":{"href":{"type":"string","format":"uri-reference","readOnly":true,"description":"Only present for image PDFs, this will be a copy of the href field in the parent\nobject.  When requested from the server it will return the image PDF as it was\nuploaded.\n"}}}]},"notifications":{"type":"boolean","description":"In a GET response, this will only appear for email and mobile PDF types.  In a POST or\nPATCH, it only makes sense for the same types.\n\nIf true, cardholder notifications will go to the telephone number or email address held by\nthis PDF.  Cardholders can have their notifications go to as many contacts as they wish.\n"}}},"CardholderCard":{"title":"Cardholder card","type":"object","properties":{"href":{"type":"string","readOnly":true,"description":"DELETE this link to delete a card.\n\nDo not specify it when creating a card.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404 in the\ncurrent versions of Command Centre.\n","format":"uri-reference"},"number":{"type":"string","description":"For a physical access card, this is its card number.  It must be unique across all cards\nof the same card type.  If you leave it blank when creating a card of a type that has a\ndecimal number format, Command Centre will use the next available number.\n\nCard numbers for a mobile credential need not be unique.  They are strings, and are not\nused by Command Centre except for display.\n\nCard numbers for digital IDs are GUIDs.\n\nThis field is mandatory for card types with text or PIV number formats.  If the card type\nhas a text card number format with a regular expression and you supply a card number that\ndoes not match that regex, Command Centre will reject your update.\n\nPIV card numbers must be the same as the card's FASC-N.  PIV-I card numbers must not.\n\nThis field is also mandatory for PIV, PIV-I, tracking tag, GovPass, and Universal Credential\n(`profileCredential`) classes.\n\nOne rule is common across all card types:  you cannot change the number of an existing\ncard or credential.\n"},"cardSerialNumber":{"type":"string","description":"The serial number (CSN, MIFARE UID) of a physical access card as a hex string without a\nleading '0x'.  The number is encoded in the traditional way, with an even number of\nnumbers and the letters from 'A' through 'F', but the '0x' is missing when it comes out\nof the API and it must be missing when you send it back.\n\nThe example shows a seven-byte serial in the order you can expect in a\nGET and should use in a PATCH or POST.  Those on NXP MIFARE cards start with 04 and end\nwith 80 or 90.\n\nFour-byte serial numbers are in the same format, but shorter: `\"EBD62D25\"`, for example.\n\nThis may not be present on very old cards.\n\nThis field was read-only until 7.90.  It is read-write in 8.00 and later.\n"},"issueLevel":{"maximum":15,"minimum":0,"type":"integer","description":"The issue level of a physical access card.  If two cards have the same number\nbut different issue levels, only the one with this issue level will gain access.\n\nIf you leave it blank when creating a card, Command Centre will pick an appropriate value.\n\nIf you increase it when modifying a card, all cards with lower issue levels will stop working.\n\nThe only credential classes for which an issue level is relevant are `card`, PIV, and\nPIV-I.  Some other credential classes currently accept and ignore issue levels, but may\nreject them in a future version.\n"},"status":{"type":"object","properties":{"value":{"type":"string","description":"This card's state, taken from the card type's state set.  The default card\nstate set contains 'Active', 'Disabled (manually)', 'Lost', 'Stolen', and\n'Damaged', or translations of those into the server's language.  Your card state sets may\ndiffer, as they are customisable. [PIV cards](piv) have extra values.\n\nIn addition to the values in the card type's state set, it will be 'Not Yet Activated'\nif the card's activation date is in the future, 'Expired' if its deactivation date has\npassed, or 'Disabled (by inactivity)' if that is the site's policy and the card has\nnot been used at a door.\n\nBecause the value of the `value` field is set by site administrators, you\nshould not use it for programmatically determining whether a card is active.\nUse `type` instead.  Use `value` for display.\n\nWhen creating or updating a card, set `value` to one of the valid card states from the\ncard state set.  Command Centre is not fussy about case.  If you omit it when you\ncreate a card, Command Centre will use the default for the card state set (which is\n'active' for the factory state set).\n\nIf you omit both this and the PIV status when creating or updating a PIV card, or omit\nthis and set the PIV status to 'normal' or 'offline', the server will set this to\n'active'.  Any other PIV status will cause the card to become inactive, regardless of\nwhat you put here.  So you really only need to set this when you are changing the PIV\nstatus to 'normal' or 'offline' but you want to disable the card.  In that case, set\n`status.value` to 'disabled (manually)'.\n"},"type":{"type":"string","description":"This will be 'pending' if the activation date (`from`) is in the future,\n'expired' if its deactivation date (`until`) is in the past, or 'inactive'\nif it is disabled for one of the reasons given in the card state.\n\nNever send `type`:  Command Centre always infers it.\n","readOnly":true,"enum":["pending","active","expired","inactive"]}},"description":"Each card has two status codes:  `value`, and `type`, covered in separate sections below.\n\n`value` is human-readable.  It comes from the _Card State Set_ configured by the site, and\ncould be adjusted according to the card's activation dates.  The REST API and this\ndocument do not cover card state sets because in most cases, the default set is\nsufficient.  See the online help for the Command Centre client if you wish to create your\nown.\n\nThe second field, `type`, comes from a fixed enumeration, and so is better suited than\n`value` for integrations.  Command Centre derives it from the card's state and activation\ndates.\n"},"type":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"},"name":{"type":"string"}},"description":"The name of the card type from the site configuration, and a link to the card type object.\n\nWhen creating a card, the href must be a card type that makes sense for the other values\nyou placed in this object.\n\nYou cannot change a card's type once it is created.\n"},"invitation":{"type":"object","properties":{"email":{"type":"string","description":"The email address to which Command Centre will send or did send an invitation for this\ncredential.\n"},"mobile":{"type":"string","description":"The telephone number to which Command Centre will send or did send an SMS containing a\nconfirmation code for this invitation.\n"},"singleFactorOnly":{"type":"boolean","description":"If you set this true Command Centre will not require a PIN or fingerprint from the\ncardholder when they enrol.  Be aware that without that second authentication factor,\nzones in modes that require a PIN and readers that always require a second factor will\nnot grant access to the cardholder.\n\n`singleFactorOnly` will only be in the result of a GET if it is true.  If it is false,\nthe field will be missing.\n","default":false},"status":{"type":"string","description":"\n- `sent` means Command Centre is waiting for the user to accept the invitation, either\n  in Gallagher Mobile Connect or another app that uses the Mobile Connect SDK.\n\n- `accepted` means that the credential is ready for use.\n\n- `notSent` means the credential is only a few seconds old or Command Centre is having\n  trouble contacting the cloud.\n\n- `expired` means that Command Centre did not receive a response in time.\n","readOnly":true,"enum":["notSent","sent","expired","accepted"]},"href":{"type":"string","description":"Mobile applications use this URL to accept this invitation.  See the Mobile Connect\nSDK documentation for how to do that in your own applications.\n\nOnly present if `status` is 'sent'.\n","format":"uri-reference","readOnly":true}},"description":"Command Centre will only return this object for a mobile credential, and you should send\nit only when creating one.  In the interests of security you cannot modify the invitation\nblock of an existing credential.  Someone's thumb might be on its way to accept it, after all.\nIf there was something wrong with it you should delete it and start afresh.\n\nWhether you should send `mobile` or `email` when creating a mobile credential depends on\nwhether you are using the Gallagher mobile apps or your own.\n\nIf you specify either `mobile` or `email` to early-version servers you must also supply the\nother so that Command Centre can send both an email invitation and a confirmation SMS.  It\nis an error to only specify one on those servers.\n\nLater versions of Command Centre made the SMS verification optional to better support\ninstallations that use the Gallagher apps but prefer not to depend on cellular connectivity.\nYou do not need to send a mobile number when creating such a credential.\n\nIf you do not give an email address Command Centre cannot send an invitation to the\ncardholder.  It will be up to another application to complete the creation of this\ncredential using the Mobile Connect SDK.\n\nYou should not send a mobile number when creating a credential for use by third-party apps\nthat use the Mobile Connect SDK.  SMS is not supported there.\n\nFor more detail on using the Mobile Connect SDK, including more complete coverage of how\nan email address and mobile number are used in the provisioning process, see its\ndocumentation at [gallaghersecurity.github.io](https://gallaghersecurity.github.io).\n"},"from":{"type":"string","description":"The start of the time period during which this card is active.  If this time is in the\nfuture, the card is not active.\n\nWhen it is not set, Command Centre acts as though it is set to a time in the distant past.\n\nWhen creating a card, leave this property out (or send `null`) to use the card type's\ndefault start time, if the card type has one.  Send an empty string `\"\"` to create the card\nwith no start time even when the card type has a default.  `from` and `until` are treated\nindependently, so leaving out `from` while sending `until` still applies the card type's\ndefault `from`.\n\nWhen modifying a card, send an empty string `\"\"` to reset it.\n\nAny value you send other than a blank string or null must be in RFC 3339 format,\nincluding a timezone.  It must be before midnight on the morning of January 1, 2100 in\nthe server's _local_ time (so servers with positive timezone offsets have maximums\nduring December 31 2099), and it must be less than or equal to the `until` time.  A\nfuture version of Command Centre will clamp too-high values to December 31 2099 and\nreturn a 2xx (success) instead of 400 in that case.\n\nThe server rejects a timestamp that is not in an acceptable format with a 400, whether the\ncard is being created or modified.\n","format":"date-time"},"until":{"type":"string","description":"The end of the time period during which this card is active.  If this time is in the past,\nthe card is not active.\n\nWhen it is not set, Command Centre acts as though it is set to a time in the distant future.\n\nWhen creating a card, leave this property out (or send `null`) to use the card type's\ndefault expiry, if the card type has one: either its fixed end time, or its duration\ncounted from the time the card is created.  Send an empty string `\"\"` to create the card\nwith no end time even when the card type has a default.  `from` and `until` are treated\nindependently, so leaving out `until` while sending `from` still applies the card type's\ndefault `until`.\n\nWhen modifying a card, send an empty string `\"\"` to reset it.\n\nAny value you send other than a blank string or null must be in RFC 3339 format,\nincluding a timezone.  It must be before midnight on the morning of January 1, 2100 in\nthe server's _local_ time (so servers with positive timezone offsets have maximums\nduring December 31 2099), and it must be greater than or equal to the `from` time.  A\nfuture version of Command Centre will clamp too-high values to December 31 2099 and\nreturn a 2xx (success) instead of 400 in that case.\n\nThe server rejects a timestamp that is not in an acceptable format with a 400, whether the\ncard is being created or modified.\n","format":"date-time"},"credentialClass":{"type":"string","description":"This indicates the type of the card.  It comes from an enumeration, and is a reliable way\nof determining the credential's type.\n\nAdded in 8.00.\n\nThe experimental `applePass` was added in 9.10.  Its name may change in future versions.\n\nThe `govPass` credential class will change its name in 9.30.\n","readOnly":true,"enum":["card","digitalId","govPass","mobile","piv","pivi","profileCredential","trackingTag","transact"]},"trace":{"type":"boolean","description":"If set, using this credential will generate an event.\n\nThis field is not in the default set so you will not see it unless you ask for it using\nthe `fields` query parameter.\n\nAdded in 8.30.\n"},"lastPrintedOrEncodedTime":{"type":"string","description":"The date and time this card was last printed or encoded.  It will not come out by\ndefault - you need to ask for it with `fields=cards.lastPrintedOrEncodedTime`.\n\nAdded in 8.40.\n","format":"date-time","readOnly":true},"lastPrintedOrEncodedIssueLevel":{"maximum":15,"minimum":1,"type":"integer","description":"The issue level of this card when it was last printed or encoded, provided it was\nnon-zero.  It will not come out by default - you need to ask for it with\n`fields=cards.lastPrintedOrEncodedIssueLevel`.\n\nAdded in 8.40.\n","readOnly":true},"lastUsedTime":{"description":"When a Gallagher controller last observed the credential in use.\n","type":"string","format":"date-time","readOnly":true},"pin":{"type":"string","description":"This is a write-only field.\nYou can use it in POSTs and PATCHes but the server will not send it to you.\n\nBeing numeric, you might be tempted to send PINs to the server without surrounding\nquotes.  However if you do that, leading zeros will be lost and the resulting behaviour\nis undefined.\n\n8.90 servers will reject your request if the card's `credentialClass` is not `card`,\n`piv`, `pivi`, or `govPass`.  More recent servers will issue a warning in that case, and\nwill not set the PIN, but will allow the rest of the update.\n\nPINs were added to the API in 8.90.\n"},"visitorContractor":{"type":"boolean","description":"This is a credential property only for GovPass, indicating that the enrolled credential is\nfor a visitor / contractor.\n\nIt is a write-once field new to 9.00.\n"},"ownedBySite":{"type":"boolean","description":"This is a credential property only for GovPass, indicating if the site is the owner of the\ncard.\n\nIt is a read-only field new to 9.00.\n","readOnly":true},"credentialId":{"type":"string","description":"Reserved for use by Gallagher applications."},"bleFacilityId":{"type":"string","description":"Reserved for use by Gallagher applications."},"encodingData":{"type":"object","readOnly":true,"properties":{"mifareClassic":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}}},"mifareDesfire":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}}}},"description":"This block contains links to the calls that return the data you will need to encode\nMIFARE DESFire and Classic cards.  For more on that see the 'Card Encoding' tag.\n\nRead-only:  the server will ignore it if you send it.\n\nAdded in 9.50.\n\nNot returned by default:  ask for it by adding `cards.encodingData` to your `fields`\nquery parameter.\n"},"deviceType":{"type":"string","enum":["applePhone","appleWatch","androidPhone","androidWatch"],"description":"Required when creating a wallet credential.  Either `applePhone` or `appleWatch` when\ncreating an Apple credential, and either `androidPhone` or `androidWatch` for Google.\n\nWallet credentials are part of the Universal Credentials feature added in 9.60.\n"}},"description":"A _card_ is an access credential, including physical cards and mobile (Bluetooth and NFC)\ncredentials and digital IDs.  The server returns it as part of a [cardholder's\ndetails](models#cardholderdetail), and you supply it when creating or modifying a card\nor credential on a cardholder.\n\nEvery card has a type, a status, and a validity period.  Its type determines other fields of\nrelevance, described below.\n\nPIV cards are complex enough to warrant a [tag of their own](piv).\n"},"CardholderAccessGroup":{"title":"Cardholder access group membership","type":"object","properties":{"href":{"type":"string","description":"This is the href of the cardholder's membership to an access group, not an access group\nitem.\n\nDELETE it to remove this group membership, and use it in the body of a PATCH to a\ncardholder to identify memberships you want to modify.\n\nNote that changing an access group membership with a PATCH will change this href, so do\nnot cache it.\n\nDELETE is the only verb you can use on this URL.  GET returns a 404.\n","format":"uri-reference"},"accessGroup":{"type":"object","description":"An object containing the group's href (which is also a link to its detail page) and,\nwhen sent by the server, the group's name.  You should not send this object when\nmodifying a group membership because you cannot change a group membership's group; you\ncan only change its dates.  But you must send it when adding a new group membership, of\ncourse, because it identifies the cardholder's new group.  Just send the group's href:\ndon't bother with the name.\n\n8.70 and later will not return the link if your operator does not have the privilege to\nview the access group (given by 'View access groups', for example).\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"status":{"type":"object","properties":{"value":{"type":"string","description":"The state of this cardholder's access group membership, in the site's language.  In an\nEnglish locale the value is the same as the type, but capitalised.\n","readOnly":true},"type":{"type":"string","description":"The state of this cardholder's access group membership.\n\nThis will be 'pending' if the activation date is set and in the future,\n'expired' if its deactivation date is set and in the past, or 'active'.\n","readOnly":true,"enum":["pending","expired","active"]}},"description":"The two fields in this block are read-only because they are determined by the `from` and\n`until` dates.\n","readOnly":true},"from":{"type":"string","description":"The start of the time period during which this group membership is active.  If this time\nis in the future, the membership will be inactive.  If null or in the past and `until`\nis null or in the future, the membership will be active.\n\nWhen sending this to a server of version 7.90.883 or earlier, use UTC with a trailing 'Z'.\nMore recent versions understand timezone offsets.\n","format":"date-time"},"until":{"type":"string","description":"The end of the time period during which this group membership is active.  If this time\nis in the past, the membership will be inactive.  If null or in the future and `from` is\nnull or in the past, the membership will be active.\n\nWhen sending this to a server of version 7.90.883 or earlier, use UTC with a trailing 'Z'.\nMore recent versions understand timezone offsets.\n","format":"date-time"}},"description":"An _access group_ is an object in Command Centre.  This section is about a connection between an access group and\na cardholder, called a _membership_.  A cardholder can be a member of many groups, and groups\ncan have any number of members.\n\nLess obvious is that a cardholder can have many memberships to the same group.  This is useful\nbecause a membership has a validity period, expressed with `from` and `until` date-times.\nOutside those moments Command Centre does not regard the cardholder as being a member of the\ngroup.  If there exists one membership with `from` in the past or unset and `until` in the\nfuture or unset, the cardholder is a member.\n\nPresence in an access group affects physical access rights and possession of PDFs, among other\nthings.\n\nThe membership object will come from the server in a cardholder's details if you ask for it, and you send it to\nthe server when modifying a cardholder's group memberships.  All uses are described below.\n"},"CardholderOperatorGroup":{"title":"Cardholder operator group membership","description":"An _operator group_ is an object in Command Centre.  The connection between an operator group\nand a cardholder is a _membership_.  A cardholder can be a member of many different operator\ngroups, and operator groups usually have more than one member, but a cardholder can only have\none membership to a given operator group at a time.  This is because an operator group\nmembership, unlike an access group membership, does not have start and end dates.\n\nPresence in an operator group affects software access.  Your REST operator, for example, must\nbe in an operator group that grants privileges otherwise it will receive nothing but 404s.\n\nAdded to the API in 8.50.\n","type":"object","properties":{"href":{"type":"string","description":"DELETE this URL or use it in the `operatorGroups` block of a [cardholder\nPATCH](cardholders#patch-api-cardholders-id-) to remove this operator group\nmembership.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n","format":"uri-reference"},"operatorGroup":{"type":"object","description":"An object containing a link to this operator group's detail page and its name.\n\nThe link will be absent from the results of a GET if your operator does not have the\nprivilege to view the operator group ('View operators' or 'Edit operators', for\nexample).\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}}}},"CardholderCompetency":{"title":"Cardholder competency","description":"A _competency_ is an object in Command Centre with some basic fields like a name and a notice\nperiod.  Its purpose is to allow a site to refuse access to cardholders who do not meet a\nspecial requirement.\n\nThis section describes the link between a cardholder and a competency.  The status of that link\ndetermines whether they \"hold\" the competency for the purposes of access control decisions at\nthe door.  When we talk about updating, adding, or deleting a competency in the cardholder\nAPI, we do not mean the competency object itself, but rather the link a cardholder has to the\ncompetency.\n\nThe link between cardholder and competency is a block in the cardholder detail.  You can\nalso request the server to add it to the results of a cardholder search by putting\n`competencies` in the `fields` parameter.\n\nWhen updating a cardholder's holding of a competency, the fields you send are slightly\ndifferent from what you receive from a cardholder GET:  you do not send read-only blocks\nsuch as `status` and `competency`, and there is a write-only field `enabled` that you can\nsupply to change the status.\n\nYou cannot use a PATCH to move the status, times, comment, and credit to a different\ncompetency.  If you need to do that, delete the cardholder's existing competency and give\nthem a new one.  You can do that in one PATCH.\n\nA cardholder can have only one link to a given competency.  Attempting to give a cardholder\na competency a second time will either fail or raise a stateful alarm and succeed, depending\non your version of Command Centre.\n","type":"object","properties":{"href":{"type":"string","description":"This is the URL of the cardholder's connection to a competency, not the URL of the\ncompetency itself.  Use it as the target of a DELETE to remove a cardholder's\ncompetency, or in the body of a cardholder PATCH to modify it.\n\nDELETE is the only verb you can use on this URL.  GET will return a 404.\n","format":"uri-reference","readOnly":true},"competency":{"type":"object","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}},"description":"This contains the competency's name and its href.\n\nAs ever, do not bother putting `name` in the body of a POST or PATCH.  The server will\nignore it.\n"},"status":{"type":"object","properties":{"value":{"type":"string"},"type":{"type":"string"}},"description":"This object contains two strings.  Both are read-only, so do not specify them when\nassigning or updating a cardholder's competency.  `value` is taken from the site's\nlanguage pack, suitable for display. `type` comes from a fixed enumeration.  It will be\n`expiryDue` or `active` when the cardholder carries this competency; anything else means\nno.  A fuller explanation follows.\n\nA competency can be disabled, expired, both, or neither.  Whether it is _enabled_ is a\nflag on the cardholder's holding of the competency.  Whether it is _expired_ is derived from an expiry\ntimestamp (accurate to the second):  if it is in the past, Command Centre considers the\ncompetency expired.\n\nA competency can also have an `enablement` date.  If that date (timestamp) passes while the\ncompetency is disabled, Command Centre will enable the competency.  It will leave the date on\nthe item for future reference, though it will not affect the competency again.\n\nIf the competency is disabled, the status `type` will be `inactive` when there is no\nenable date or it is in the past, and `pending` when the enable date is in the future\n(i.e., there is an automatic re-enablement coming).\n\nIf the competency is not disabled, the `expiry` time is important.  If it is in the past,\n`type` will be `expired`.\n\nAll of those cases are negative.  Two remain, when our cardholder is blessed with an\nenabled and active competency.  `type` will be `expiryDue` if the `expires` time is in the\nfuture but within the competency's advance notice period, or `active` if the `expires`\ntime is beyond the advance notice period or not set at all.\n\nYou can see and set the enabled flag, the enable date, and the expiry date via this\nAPI.\n\nWhen creating a cardholder or updating a competency on an existing cardholder, you should\nset the `enabled` field one way or the other, and the `enablement` and `expiry` dates if\nyou wish.  They will determine the contents of this status block.\n\nHere it is in table form.  The first two rows are the positive cases, when Command Centre\nwould grant access to a competency-enforced zone.\n\n| Enabled flag | Enablement date | Expiry date | `status.type`\n| ------------ | -------------- | --------| ----- |\n| true | - | Far future | active |\n| true | - | Near future | expiryDue |\n| true | - | Past | expired |\n| false | Future | - | pending |\n| false | Past | - | inactive |\n| false | Unset | - | inactive |\n","readOnly":true},"disabled":{"type":"boolean","readOnly":true,"deprecated":true,"description":"Do not use this field.  Instead, take the state of a cardholder's competency from\n`status.type`.  This field is misleading on its own:  even when it is true, the\ncompetency might still be inactive.\n"},"enabled":{"type":"boolean","writeOnly":true,"description":"This is a write-only field.  You will not receive it from the GET.  It changes the\ncompetency between enabled (status type `expired`, `expiryDue`, or `active`) and\ndisabled (status type `inactive` or `pending`).  Set it to false in a PATCH if you want\nto disable the competency now, particularly if you are setting a future enablement date.\n"},"expiryWarning":{"type":"string","description":"The time at which the cardholder will (or did) receive a warning about the\ncompetency expiring.  If this time is set and in the past but the competency has\nnot yet expired and is still enabled, the status type will be `expiryDue`.\n","format":"date-time","readOnly":true},"expiry":{"type":"string","description":"The time at which the competency will expire.  If this time is set, in the past, and the\ncompetency is enabled, status type will be `expired`.\n","format":"date-time"},"enablement":{"type":"string","description":"The time at which the competency will be re-enabled.  If set and in the future, and the\ncompetency is disabled, the status type will be `pending`.\n\nYou can set this on an enabled competency but it has no effect there, so with this field\nyou would generally also set `enabled: false`.\n","format":"date-time"},"comment":{"type":"string","description":"The comment appears in the management clients when viewing the cardholder.\n"},"limitedCredit":{"type":"boolean","description":"If false, Command Centre's 'Pre-pay Car Parking' feature (available under its own licence)\nwill not reduce the current credit.\n\nThis field will be in the results if its value is true.\n"},"credit":{"type":"integer","description":"The balance, or amount of credit left on this competency for use by Pre-pay Car Parking.\nIt can be negative.\n\nThis field will be in the results if its value is not zero.\n"}}},"CardholderRelationship":{"title":"Cardholder relationship","description":"A _role_ is an object in Command Centre.  Roles are usually named using nouns such as\n'supervisor', 'manager', or 'team leader'.\n\nThe operator clients and the REST API allow you to create a link between two\ncardholders, called a relationship, using a role.  The link is directional:  we\nrefer to the cardholder who 'has' the role as the child, and the cardholder who\nperforms or 'is' the role as the parent.\n\nA parent can perform a role for any number of child cardholders, but a child can only have one\nrelationship (parent) for each role.  Command Centre will reject your submission if you try to\ncreate a relationship when one already exists for the same child and role.\n\nFor example, a role on your system might be 'supervisor'.  A cardholder can be a supervisor\nfor any number of others, but will only have one supervisor.\n\nLoops are possible:  two cardholders can supervise each other.\n\nThe REST API manages relationships through the child cardholder.  You can create them at the\nsame time as creating the child in a POST, or add them later in a PATCH.\n\nThere is currently no way to list all a cardholder's children in one request (everyone a\nparticular cardholder is supervising, for example).  You would achieve that by iterating\nthrough all cardholders, after re-reading the efficiency tips, and checking their\nrelationships - quite easily done in JSONPath.\n\nThis section describes the object you receive in a cardholder's detail page and you will send\nin a POST or PATCH.  The child cardholder does not appear in it because he or she is\nidentified by the URL of the request.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"This is the href of the link between two cardholders.  DELETE it, or put it in\n`relationships.remove.href` of a PATCH directed at the child cardholder, to sever the\nrelationship.  Specify it in `relationships.update.href` of a PATCH to the child\ncardholder to update the relationship.\n\nDo not specify it when creating a new relationship.  The server generates it for you.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n"},"role":{"type":"object","description":"This is the role that the parent identified in the `cardholder` block performs for the\ncardholder identified by the request URL.\n\nOnce set, this cannot be changed.  Command Centre will ignore it if you send it\nin an update PATCH.  If you need to swap a parent from one role to another, send\nan add and a delete in the same PATCH.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"cardholder":{"type":"object","description":"The href and name of the cardholder that performs this role.\n\nThe three name fields are read-only:  Command Centre sends them to you in the body of a\nGET but will ignore them if you send them in the body of a POST or PATCH.\n\nThis block and the href in it are unnecessary when deleting a relationship.  The href is\nrequired when creating one, and optional when updating, but strongly advised since the\ncardholder is the only thing about a relationship you can change.\n\n`firstName` and `lastName` appeared in 8.20.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for the parent cardholder."},"firstName":{"type":"string","readOnly":true},"lastName":{"type":"string","readOnly":true}}}}},"CardholderLocker":{"title":"Cardholder locker","description":"These appear in an array in a cardholder detail, showing the cardholder's allocated lockers.\nEach allocation has `from` and `until` dates, much like cards and access group memberships,\noutside of which the allocation is inactive.\n\nA locker can have allocations to more than one cardholder, with separate or overlapping\nperiods.  Unlike access groups, however, one cardholder cannot have more than one allocation\nto the same locker.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"DELETE this to end a cardholder's use of a locker, or use it to identify the\nallocation you wish to modify in a [cardholder\nPATCH](cardholders#patch-api-cardholders-id-).\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n"},"locker":{"description":"The href in this object is a link to the allocated locker, and is the identifier to use\nwhen allocating the same locker to another cardholder.\n\nThe object also contains the name and short name of the locker, and the name and\nidentifying href of its bank.\n","type":"object","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for a locker."},"shortName":{"type":"string","readOnly":true},"lockerBank":{"type":"object","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}}}},"from":{"description":"The start of the time period during which the cardholder has access to this locker.\n\nSend an empty string `\"\"` to reset it.\n","type":"string","format":"date-time"},"until":{"description":"The end of the time period during which the cardholder has access to this locker.\n\nSend an empty string `\"\"` to reset it, making the allocation permanent.\n","type":"string","format":"date-time"}}},"CardholderElevatorGroup":{"title":"Cardholder elevator group","description":"These appear in an array in a cardholder detail, showing the cardholder's elevator group\nproperties, and can go into the body of a POST or PATCH to set a cardholder's elevator group\nproperties.\n\nA cardholder can have one default floor per elevator group. The elevator system will\nprepare a car to carry that cardholder to the access zone shown here when the cardholder\nbadges a card at an appropriately configured kiosk.\n\nElevator system features can be activated by enabling the feature for an elevator group.\nFor example, 'VIP features' gives exclusive access to an elevator car to the passenger.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"The href of this cardholder's elevator group entry.  DELETE this to remove it from the\ncardholder.\n"},"elevatorGroup":{"type":"object","description":"The href and name of the elevator group for which this cardholder has a default\nfloor or passenger type.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"accessZone":{"type":"object","description":"The href and name of the access zone (floor) to which this cardholder is most\nlikely to want to travel after entering the group's main elevator lobby. This \nproperty will be missing if the cardholder does not have a default floor for this elevator group.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"enableCaptureFeatures":{"type":"boolean","description":"Cardholders can select and recall specific elevators to specific floors using\na kiosk. Once captured, the elevator car can be placed on independent service to\ngive users control of the car to clean the interior or perform maintenance.\n"},"enableCodeBlueFeatures":{"type":"boolean","description":"A special elevator mode which is commonly found in hospitals. It allows an elevator\nto be summoned to any floor for use in an emergency situation.\n"},"enableExpressFeatures":{"type":"boolean","description":"Allows the cardholder to program selected elevators to cycle continuously between two\nfloors for a pre-determined duration. For example, this feature can help hotels transport\nfood efficiently from their kitchen to a ballroom on another floor. You can also prevent\nother guests from boarding to provide your banquet guests with VIP treatment.\n"},"enableServiceFeatures":{"type":"boolean","description":"Service personnel can use this function to call an empty elevator and ride it nonstop\nto their destination floor. The user simply registers a call via a card swipe or PIN entry\nthat is pre-programmed to grant access.\n"},"enableService2Features":{"type":"boolean","description":"Service personnel can use this function to call an empty elevator and ride it nonstop\nto their destination floor. The user simply registers a call via a card swipe or PIN entry\nthat is pre-programmed to grant access.\n"},"enableService3Features":{"type":"boolean","description":"Service personnel can use this function to call an empty elevator and ride it nonstop to\ntheir destination floor. The user simply registers a call via a card swipe or PIN entry\nthat is pre-programmed to grant access.\n"},"enableVipFeatures":{"type":"boolean","description":"VIP operation allows cardholders to swipe a card or enter a PIN to isolate the elevator\nand provide uninterrupted access to their designated floor.\n"}}}}},"paths":{"/api/cardholders":{"post":{"tags":["Cardholders"],"summary":"Create a cardholder","description":"This creates a new cardholder, including his or her cards, group memberships, personal data,\ncompetencies, and roles.\n\n[Do not code this URL](forward-compatibility-hateoas) into your application.  Take\nit from the `href` field in the `features.cardholders.cardholders` section of `/api`.\n\nThe POST expects a document in the same format as the [cardholder\ndetail](models#cardholderdetail).  Many fields are optional, of course, and some (like\nthe last successful access time) do not make sense when creating a cardholder.  See the\n[cardholder POST example](models#cardholderpost) for details.\n\nYou will achieve better performance if you combine all you want to achieve into one POST,\nrather than creating the cardholder bare with a POST then adding cards, groups, PDFs, etc.,\nwith PATCHes later.\n\nWhen successful it returns a location header containing the address of the new cardholder.\n\nNote that you can only create one cardholder per POST.\n","requestBody":{"description":"This can be a large object as shown in the body model, or a tiny one, because the only\nfields you must have in the POST are the division and either the first or last name.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardholderPOST"}}},"required":true},"responses":{"201":{"description":"Success.\n","headers":{"location":{"description":"The href of the new cardholder.","schema":{"type":"string","format":"uri-reference"}}}},"400":{"description":"The body of the POST did not describe a valid cardholder.\n\nIf you see 'Data has not been entered for this Personal Data Field' in the\nresponse body, you have attempted to create a cardholder in an access group\nthat has a required Personal Data Field but have not supplied a value for that\nPDF.\n\nIf you see 'Invalid cardholder', the server could not parse the JSON in the body of your\nPOST.  Remember to quote all strings, especially those that contain @ symbols.\n"},"403":{"description":"The operator does not have a privilege that allows creating cardholders, or you\nattempted to set a field for which the operator has no privilege (probably `notes`), or\nthe server has reached its licensed limit of cardholders.\n"}}}}}}
```

## Get details of a cardholder

> This retrieves one cardholder from Command Centre.  It does not return all the fields\
> available (there are too many) so, much like the cardholder search, it accepts a \`fields\`\
> parameter where you specify the fields you need.\
> \
> \[Do not use]\(forward-compatibility-hateoas) a\
> cardholder's ID to build the URL yourself:  follow the href in the \[cardholder\
> search]\(models#cardholders) to get here.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"schemas":{"CardholderDetail":{"title":"Cardholder detail","description":"[/api/cardholders/{id}](cardholders#get-api-cardholders-id-) returns one of these, and you\nsubmit [parts of one](models#cardholderpost) in a POST to create a cardholder.\n\nYou may find it contains a block called `updates`.  This is reserved for future\ndevelopment and its behaviour will change in later versions of Command Centre.\n","allOf":[{"$ref":"#/components/schemas/CardholderSummary"},{"$ref":"#/components/schemas/CardholderCommonFields"},{"type":"object","title":"Detail fields","description":"Fields unique to the cardholder detail.","properties":{"lastSuccessfulAccessTime":{"type":"string","description":"The date and time of the last successful card event or operator-initiated movement\n(using a tag board or [this\nAPI](cardholders#post-api-cardholders-id-update_location)).\n","format":"date-time","readOnly":true},"lastSuccessfulAccessZone":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"},"name":{"type":"string"}},"description":"The last zone the cardholder entered.\n\nIf their last movement was to the 'outside' (through a door that has no access zone\nconfigured for that direction of travel) this field will be missing but\n`lastSuccessfulAccessTime` will be present.\n\nIf the cardholder is a visitor and their last\nmovement was to leave the site, this will be their visit's reception, not an access\nzone.  8.50 and later will present only the name of the reception and withhold the\nhref in that case.\n\n**Change is coming.**\n\nA future version of Command Centre will add the href field even if the item is a\nreception.  So you can tell the difference, it will add a field called\n`canonicalTypeName` which will have one of two values:  `accesszone` or `reception`.\n","readOnly":true},"serverDisplayName":{"type":"string","description":"If you are running a multi-server installation and this item is homed on a remote server, this\nfield will contain the name of that server.  This field is missing from items that are held on\nthe machine that served the API request.  Added in 8.40.\n\nThis is a read-only field.  The server will ignore it if you send it.\n","readOnly":true},"usercode":{"type":"string","description":"This field is write-only, so you will not see it in the results of a GET.  It appears\nhere because you can send it in the body of a POST or PATCH.\n\nSet it to the empty string `\"\"` to clear the cardholder's user code.\n\nIf it is not empty it must be a string of at least four digits.  It may need to be\nlonger, because the minimum length is set by your site configuration.\n\nYou will find that \"0000\" does not work.\n"},"operatorPassword":{"type":"string","description":"The password this user must supply when logging in to the interactive clients, if they\nare not using Windows integrated authentication.\n\nCommand Centre imposes password length and complexity restrictions.  If your password\ndoes not meet those requirements, the entire update will fail.  The body of the 4xx\nresponse will tell you why.\n\nLike the user code, this is write-only so you will not see it in the results of a GET.\n"},"cards":{"type":"array","description":"All the cards for this cardholder.  Note that even though Command Centre\ncalls these things 'cards', they include other types that do not require a physical\ncard, such as digital IDs and mobile credentials.\n","items":{"$ref":"#/components/schemas/CardholderCard"}},"accessGroups":{"type":"array","description":"All the cardholder's access group memberships.  This does not list memberships the\ncardholder inherits through other groups.\n\nThe server property 'Show all cardholder access group memberships' does not affect the\nAPI as it affects the configuration and operational clients, so this field only\nappears if the operator has permission to see access group memberships.  That is\ngranted by a privilege such as 'View cardholders'.\n","items":{"$ref":"#/components/schemas/CardholderAccessGroup"}},"operatorGroups":{"type":"array","description":"All the cardholder's operator group memberships.  Added in 8.50.\n","items":{"$ref":"#/components/schemas/CardholderOperatorGroup"}},"competencies":{"type":"array","description":"Every competency a cardholder possesses.  There is quite a lot there:  see the schema\ndefinition for detailed explanation.\n","items":{"$ref":"#/components/schemas/CardholderCompetency"}},"edit":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}},"description":"Reserved for internal use.  It is a link to [another\ncall](cardholders#get-api-cardholders-id-edit) that helps interactive\nadministrative clients.  Do not send it when creating or editing a cardholder.\n","readOnly":true},"updateLocation":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}},"description":"Link to send a [POST](cardholders#post-api-cardholders-id-update_location) to when\nyou want to update the location of this cardholder.  Added in 8.20.\n","readOnly":true},"relationships":{"type":"array","description":"This is a list of the roles that other cardholders perform for this cardholder.\n\nSince only one cardholder can perform a given role for another, there will be no more\nelements in this array than there are roles on the site.\n\nThe example shows that this cardholder has one supervisor.\n","items":{"$ref":"#/components/schemas/CardholderRelationship"}},"lockers":{"type":"array","description":"All the cardholder's locker assignments.\n","items":{"$ref":"#/components/schemas/CardholderLocker"}},"elevatorGroups":{"type":"array","description":"The cardholder's elevator group properties. They may have one per elevator group.\n\nAdded in 8.50.\n","items":{"$ref":"#/components/schemas/CardholderElevatorGroup"}},"updates":{"deprecated":true,"type":"object","properties":{},"description":"Cardholders include an `updates` block, as other items do, that allows monitoring one\ncardholder per TCP session in a long poll.  However the [Cardholder\nchanges](cardholder-change-tracking) methods are a far more efficient way of monitoring\nyour cardholders.\n"},"redactions":{"type":"array","description":"All the cardholder's scheduled redactions.\n\nBecause this is relatively expensive to compute, this field does not appear by\ndefault.  You must ask for it with `fields=redactions` or\n`fields=redactions.href,redactions.type,redactions.when`, for example.\n\nAdded in 8.80.\n","items":{"$ref":"#/components/schemas/CardholderRedaction"}},"accessibilities":{"description":"The cardholder's accessibility settings in an array of three elements.\n\nNew to 9.50.\n","type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["disableCipherPad","useExtendedAccessTime","pinExemption"],"description":"\n* `disableCipherPad` means this cardholder will not have the numbers on an\n  alarms terminal scrambled when the terminal is configured as a cipher pad.\n  Usually the reason is a vision impairment making a randomised keypad\n  impracticable.\n\n* `useExtendedAccessTime` increases the time a door remains unlocked and the\n  amount of time that can pass before raising a 'door open too long' alarm.\n\n* `pinExemption` bypasses PIN entry.  Users with this accessibility enabled who\n  badge a card at a reader that normally requires PIN entry will not be prompted\n  to enter a PIN.\n"},"enabled":{"type":"boolean","description":"If false, the accessibility is disabled.  If true, it depends on `from` and\n`until`.\n"},"from":{"type":"string","format":"date-time","description":"If set and in the future, the accessibility will not be active until then.  If\nnot set or in the past, the accessibility will depend on `enabled`.\n"},"until":{"type":"string","format":"date-time","description":"If set and in the past, the accessibility will have been inactive since then.\nIf not set or in the future, the accessibility will depend on `enabled`.\n\nIf you send it, it must be later than both the `from` date, if sent, and the\ncurrent time.\n"}}}}},"patternProperties":{"^@.+":{"description":"An example Personal Data Field value.\n\nAll PDFs except images appear at the top level of a cardholder in a field named\nafter the PDF with a leading '@'.\n\nDate PDFs will contain a date-time with the time part set to midnight.\n\nImage PDFs appear as a block containing an\nhref. GETting that href will return the image data with an appropriate\ncontent-type header.\n\nPut your PDF values here when creating or modifying a cardholder.  Note that to\nhold a PDF value the cardholder must be a member of an access group that has that\nPDF attached to it.  In this example, being a member of the special projects\ngroup has granted our Mr. Boothroyd the student ID and photo PDFs.\n\nTo set an image PDF, Base64-encode the image and send it as a string.  There is\none in the [cardholder POST example](models#cardholderpost).\n\nDate PDFs are only accurate to a day, so version 8.75 and later will ignore the\ntime and time zone components if you send them.  8.70 and earlier will first\nperform a timezone correction then convert it to UTC before truncating the time,\nwhich might shift the date back a day if you specified a positive timezone\noffset.  To be sure that does not happen on a server older than 8.75, put\n`T00:00:00Z` on the end of your date.\n"}}}]},"CardholderSummary":{"title":"Cardholder summary","type":"object","properties":{"href":{"type":"string","format":"uri-reference","readOnly":true,"description":"A link to a [cardholder detail](models#cardholderdetail) object for this cardholder.\nThis is Command Centre's identifier for this cardholder:  use it whenever you need to\nspecify a cardholder in REST operations.\n"},"id":{"type":"string","description":"An alphanumeric identifier, unique to the server.  No API calls use cardholder IDs.\n\nDeprecated.  Use the href instead.\n","readOnly":true},"firstName":{"type":"string"},"lastName":{"type":"string"},"shortName":{"maxLength":16,"type":"string","description":"If you supply a string that is too long when creating or updating\na cardholder, Command Centre will truncate it.\n"},"description":{"type":"string"},"authorised":{"type":"boolean","description":"This is called 'Cardholder Authorised' in the administrative clients.  If false, Command\nCentre will deny card access decisions for this cardholder.\n\n`authorised` is false by default.  If you want a new cardholder to open doors, be sure to\nset it to true in your POST.\n\nYou need the 'Edit cardholders' privilege to set this true.  In versions prior to 8.80\nyour operator also needed that privilege to set it false, but in 8.80 and later\n'De-authorise cardholder' is enough on its own.\n"}},"description":"The cardholder search at `/api/cardholders` returns an array of these.  It is a subset of what\nyou get from a cardholder's detail page at `/api/cardholders/{id}` (linked as the href in this\nobject), to be more suitable for large result sets.\n"},"CardholderCommonFields":{"title":"Cardholder common details","description":"The fields shared by the cardholder GET, POST, and PATCH.\n","type":"object","properties":{"disableCipherPad":{"type":"boolean","deprecated":true,"description":"True if this cardholder should not have the numbers on an alarms terminal scrambled\nwhen the terminal is configured as a cipher pad.  Usually the reason is a vision\nimpairment, making a randomised keypad impracticable.\n\nDeprecated from 9.50 in favour of the `disableCipherPad` element in the accessibilities\narray.  This field can still be set for backwards compatibility, but only set one of the\ntwo:  setting them to disagreeing values will earn you a 400.\n"},"division":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}},"description":"The division containing this cardholder.  You must send this when creating a\ncardholder.\n"},"notes":{"type":"string","description":"Free-form text.\n\nThe 'Edit Cardholder Notes' privilege lets you change cardholder notes however you\nlike, but 'Add Cardholder Notes' only lets you add more to the end.  To do that, read\nthe existing `notes` field, append your text, and send it back in a PATCH.  If you try\nto change the existing text the server will respond with a 4xx.\n\nYou will need one of those two privileges to edit notes.  'Create Cardholders' and\n'Edit Cardholders' let you set most cardholder fields, but not notes.\n\nBecause of the potential size of the notes field, the server does not return it unless\nyou ask for it with `fields=notes`.\n"},"notifications":{"type":"object","description":"This block shows the cardholder's notification settings.  It deserves some explanation.\n\nThe `enabled` bool is correct at the time of your call.  If false, CC will not be\nsending your cardholder any notifications.\n\nThe `from` and `until` date-times show if and when `enabled` will change.  When the\n`from` date-time passes, `enabled` will flip, the `from` date-time will take the\n`until` value, and `until` will become null.  This continues until `from` is null.\n\nYou can set `from` with a null `until`, but not `until` with a null `from`.  They must\nboth be in the future or null, and `from` must be before `until`.\n\nIf you want to simply turn a person's notifications on or off without any changes\nscheduled for the future, set `enabled` true or false and both `from` and `until`\nnull.","properties":{"enabled":{"type":"boolean"},"from":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"}}},"oidcLoginEnabled":{"type":"boolean","description":"If true, and they have an `oidcUserId`, the operator can log in to the Command Centre\nclient using their Entra ID username.\n\nAdded in 9.40.\n"},"oidcUserId":{"type":"string","description":"This user's Entra ID username.  This should be the operator's Entra ID 'User Principal\nName' found in the overview of the user's details in Entra ID Admin Center.\n\nSend the empty string `\"\"` to clear it.\n\nAdded in 9.40.\n"},"operatorLoginEnabled":{"type":"boolean","description":"True if this operator can log in to the interactive Command Centre clients.  Note that\na cardholder also needs a privilege to use the Configuration Client.\n"},"operatorUsername":{"type":"string","description":"The name that the operator uses when logging in to the interactive clients.  Absent if blank.\n\nSend the empty string `\"\"` to clear it.\n"},"operatorPasswordExpired":{"type":"boolean","description":"If true, the interactive clients will request a new password the next time this person\nlogs in.\n"},"personalDataDefinitions":{"type":"array","description":"All the personal data definitions and values for this cardholder.  It is an array of\nobjects, each with one key/value pair.  The key is the name of the PDF preceded by\nan '@'; the value is an object containing the definition of the PDF (common for all\ncardholders) and the value for this cardholder.  The `value` block of an image PDF will\ncontain an href to the image.\n\nYour visibility of a cardholder's PDF value depends entirely on the PDF's access\nsettings in your operator groups, or if it has none of those, its default privilege.\nThe PDF's division is irrelevant.  So if you were expecting a PDF value here when none\narrived, ensure the cardholder has a value for that PDF (because the server does not\nsend nulls) then check the PDF's access settings in all of your operator groups, then\nthe default visibility on the PDF item itself.\n\nWhen you send this array in a POST or PATCH, Command Centre will take the\n`notifications` flag from here.  It can take `value` from here too, but if you also\nsend a field in the root of the cardholder with the name of the PDF preceded by an\n'@', that one wins.\n\nTo request this block using the `fields` parameter, use `personalDataFields` (note\n'fields' not 'definitions').  Doing so will not only give you the\n`personalDataDefinitions` block, but also the values at the root level (with their names\npreceded by '@'-signs).\n","items":{"$ref":"#/components/schemas/CardholderPDF"}},"windowsLoginEnabled":{"type":"boolean","description":"If true, and they have a Windows username, this user can log in using Windows\nintegrated authentication.\n"},"windowsUsername":{"type":"string","description":"This user's Windows username.  For an example of the format that will work in your\norganisation, look in the 'Windows logon' field of the 'Operator configuration' tab of\na cardholder's properties in the Configuration Client.\n\nSend the empty string `\"\"` to clear it.\n"}}},"CardholderPDF":{"title":"Cardholder PDF","description":"A personal data definition and its value for a cardholder.  Each definition is an object\ncontaining one property named after the PDF (plus a leading '@'), which in turn contains its\nvalue and notifications flag for the containing cardholder and an object containing some of\nthe PDF definition's basic fields.\n\nThis is returned as part of a [cardholder](models#cardholderdetail), and you can send it\nin a [POST](models#cardholderpost) or a\n[PATCH](models#cardholderpatch) to set the PDF's value and notification flag for\na cardholder.  The server will ignore the `definition` block if you send it back.  It uses the\nname of the block (minus its leading `@`) to find the PDF to change.\n\nWhile you can use this structure to change the value of a cardholder's PDF, it may be simpler\nto put a property in the root of the payload named after the PDF with a leading '@'.\n","type":"object","additionalProperties":{"$ref":"#/components/schemas/CardholderPDFContent"}},"CardholderPDFContent":{"title":"Cardholder PDF content","type":"object","properties":{"href":{"type":"string","description":"GET this link to receive this cardholder's value for this PDF.  If it is an image you will\nreceive the raw image data with an appropriate content-type.  A browser will render it\ncorrectly.\n\nIf the PDF is not an image the content type will be text/plain, encoded (like\neverything else in this API) as UTF-8.  But you should only use it for image PDFs\nbecause:\n\n* Values for scalar PDF types (everything except images) are also given to you in the\n  `value` field in this object, described below.\n\n* The format of date PDFs varies with the Command Centre version.  Old servers return\n  it in a format that depends on the server's locale.  This was a bug.  Newer servers\n  use ISO 8601.\n\n* If you learn this URL while a cardholder has the PDF, then the cardholder loses\n  it, then you GET the URL, results will vary depending on the server version.  The\n  correct result is a 404, and if the PDF is an image or the server is recent you\n  will see that.  But old servers may return a blank string or even a 500 for\n  non-image PDFs.\n\nThis field will be absent if the cardholder does not have a value for the PDF.\n\nIt is read-only:  do not send it in a PATCH or POST.\n","format":"uri-reference","readOnly":true},"definition":{"type":"object","properties":{"href":{"type":"string","description":"This is the href of the PDF's definition, common to all cardholders who hold a value\nfor this PDF.\n","format":"uri-reference"},"name":{"type":"string","description":"The PDF's name, without an '@' prefix.\n"},"id":{"type":"string","description":"Short alphanumeric identifier used elsewhere in the API to filter cardholder searches\nand add PDF values to alarm and event GETs.\n"},"type":{"type":"string","enum":["string","image","strEnum","numeric","date","address","phone","email","mobile"]}},"description":"The definition object is read-only:  do not send it in a PATCH or POST.\n","readOnly":true},"value":{"description":"In a GET response for text, numeric, and date PDFs this will be a scalar containing the\nvalue, but for image PDFs it will be an object containing a copy of the `href` field\nabove.  For date PDFs the string will comply with RFC 3339, but may or may not contain a\ntime depending on the version of Command Centre.  Newer versions omit the `T` separator\nand everything after it.  If there, it will be midnight UTC.\n\nIn versions up to and including 8.60, if a cardholder did not have an image PDF captured\n(in other words they were a member of an access group that included an image PDF, but they\ndid not have a value for it) the API returned the string 'Not captured' in the `value`\nfield.  Versions 8.70 and later will not return the `value` field at all if there is no image.\n\nIn a POST or PATCH send text PDFs (including dates) as strings, numeric PDFs as numbers or\nstrings that parse to numbers, and image data Base64-encoded into a string.\n","oneOf":[{"type":"integer","title":"Numeric","description":"The scalar value of the numeric PDF."},{"type":"string","title":"Text or date","description":"The scalar value of the text or date PDF."},{"type":"object","title":"Image","description":"An object containing an href which, when requested from the server, returns the\nimage PDF.  The server does not transcode images:  it will arrive as the same\ncontent type as uploaded.\n","properties":{"href":{"type":"string","format":"uri-reference","readOnly":true,"description":"Only present for image PDFs, this will be a copy of the href field in the parent\nobject.  When requested from the server it will return the image PDF as it was\nuploaded.\n"}}}]},"notifications":{"type":"boolean","description":"In a GET response, this will only appear for email and mobile PDF types.  In a POST or\nPATCH, it only makes sense for the same types.\n\nIf true, cardholder notifications will go to the telephone number or email address held by\nthis PDF.  Cardholders can have their notifications go to as many contacts as they wish.\n"}}},"CardholderCard":{"title":"Cardholder card","type":"object","properties":{"href":{"type":"string","readOnly":true,"description":"DELETE this link to delete a card.\n\nDo not specify it when creating a card.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404 in the\ncurrent versions of Command Centre.\n","format":"uri-reference"},"number":{"type":"string","description":"For a physical access card, this is its card number.  It must be unique across all cards\nof the same card type.  If you leave it blank when creating a card of a type that has a\ndecimal number format, Command Centre will use the next available number.\n\nCard numbers for a mobile credential need not be unique.  They are strings, and are not\nused by Command Centre except for display.\n\nCard numbers for digital IDs are GUIDs.\n\nThis field is mandatory for card types with text or PIV number formats.  If the card type\nhas a text card number format with a regular expression and you supply a card number that\ndoes not match that regex, Command Centre will reject your update.\n\nPIV card numbers must be the same as the card's FASC-N.  PIV-I card numbers must not.\n\nThis field is also mandatory for PIV, PIV-I, tracking tag, GovPass, and Universal Credential\n(`profileCredential`) classes.\n\nOne rule is common across all card types:  you cannot change the number of an existing\ncard or credential.\n"},"cardSerialNumber":{"type":"string","description":"The serial number (CSN, MIFARE UID) of a physical access card as a hex string without a\nleading '0x'.  The number is encoded in the traditional way, with an even number of\nnumbers and the letters from 'A' through 'F', but the '0x' is missing when it comes out\nof the API and it must be missing when you send it back.\n\nThe example shows a seven-byte serial in the order you can expect in a\nGET and should use in a PATCH or POST.  Those on NXP MIFARE cards start with 04 and end\nwith 80 or 90.\n\nFour-byte serial numbers are in the same format, but shorter: `\"EBD62D25\"`, for example.\n\nThis may not be present on very old cards.\n\nThis field was read-only until 7.90.  It is read-write in 8.00 and later.\n"},"issueLevel":{"maximum":15,"minimum":0,"type":"integer","description":"The issue level of a physical access card.  If two cards have the same number\nbut different issue levels, only the one with this issue level will gain access.\n\nIf you leave it blank when creating a card, Command Centre will pick an appropriate value.\n\nIf you increase it when modifying a card, all cards with lower issue levels will stop working.\n\nThe only credential classes for which an issue level is relevant are `card`, PIV, and\nPIV-I.  Some other credential classes currently accept and ignore issue levels, but may\nreject them in a future version.\n"},"status":{"type":"object","properties":{"value":{"type":"string","description":"This card's state, taken from the card type's state set.  The default card\nstate set contains 'Active', 'Disabled (manually)', 'Lost', 'Stolen', and\n'Damaged', or translations of those into the server's language.  Your card state sets may\ndiffer, as they are customisable. [PIV cards](piv) have extra values.\n\nIn addition to the values in the card type's state set, it will be 'Not Yet Activated'\nif the card's activation date is in the future, 'Expired' if its deactivation date has\npassed, or 'Disabled (by inactivity)' if that is the site's policy and the card has\nnot been used at a door.\n\nBecause the value of the `value` field is set by site administrators, you\nshould not use it for programmatically determining whether a card is active.\nUse `type` instead.  Use `value` for display.\n\nWhen creating or updating a card, set `value` to one of the valid card states from the\ncard state set.  Command Centre is not fussy about case.  If you omit it when you\ncreate a card, Command Centre will use the default for the card state set (which is\n'active' for the factory state set).\n\nIf you omit both this and the PIV status when creating or updating a PIV card, or omit\nthis and set the PIV status to 'normal' or 'offline', the server will set this to\n'active'.  Any other PIV status will cause the card to become inactive, regardless of\nwhat you put here.  So you really only need to set this when you are changing the PIV\nstatus to 'normal' or 'offline' but you want to disable the card.  In that case, set\n`status.value` to 'disabled (manually)'.\n"},"type":{"type":"string","description":"This will be 'pending' if the activation date (`from`) is in the future,\n'expired' if its deactivation date (`until`) is in the past, or 'inactive'\nif it is disabled for one of the reasons given in the card state.\n\nNever send `type`:  Command Centre always infers it.\n","readOnly":true,"enum":["pending","active","expired","inactive"]}},"description":"Each card has two status codes:  `value`, and `type`, covered in separate sections below.\n\n`value` is human-readable.  It comes from the _Card State Set_ configured by the site, and\ncould be adjusted according to the card's activation dates.  The REST API and this\ndocument do not cover card state sets because in most cases, the default set is\nsufficient.  See the online help for the Command Centre client if you wish to create your\nown.\n\nThe second field, `type`, comes from a fixed enumeration, and so is better suited than\n`value` for integrations.  Command Centre derives it from the card's state and activation\ndates.\n"},"type":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"},"name":{"type":"string"}},"description":"The name of the card type from the site configuration, and a link to the card type object.\n\nWhen creating a card, the href must be a card type that makes sense for the other values\nyou placed in this object.\n\nYou cannot change a card's type once it is created.\n"},"invitation":{"type":"object","properties":{"email":{"type":"string","description":"The email address to which Command Centre will send or did send an invitation for this\ncredential.\n"},"mobile":{"type":"string","description":"The telephone number to which Command Centre will send or did send an SMS containing a\nconfirmation code for this invitation.\n"},"singleFactorOnly":{"type":"boolean","description":"If you set this true Command Centre will not require a PIN or fingerprint from the\ncardholder when they enrol.  Be aware that without that second authentication factor,\nzones in modes that require a PIN and readers that always require a second factor will\nnot grant access to the cardholder.\n\n`singleFactorOnly` will only be in the result of a GET if it is true.  If it is false,\nthe field will be missing.\n","default":false},"status":{"type":"string","description":"\n- `sent` means Command Centre is waiting for the user to accept the invitation, either\n  in Gallagher Mobile Connect or another app that uses the Mobile Connect SDK.\n\n- `accepted` means that the credential is ready for use.\n\n- `notSent` means the credential is only a few seconds old or Command Centre is having\n  trouble contacting the cloud.\n\n- `expired` means that Command Centre did not receive a response in time.\n","readOnly":true,"enum":["notSent","sent","expired","accepted"]},"href":{"type":"string","description":"Mobile applications use this URL to accept this invitation.  See the Mobile Connect\nSDK documentation for how to do that in your own applications.\n\nOnly present if `status` is 'sent'.\n","format":"uri-reference","readOnly":true}},"description":"Command Centre will only return this object for a mobile credential, and you should send\nit only when creating one.  In the interests of security you cannot modify the invitation\nblock of an existing credential.  Someone's thumb might be on its way to accept it, after all.\nIf there was something wrong with it you should delete it and start afresh.\n\nWhether you should send `mobile` or `email` when creating a mobile credential depends on\nwhether you are using the Gallagher mobile apps or your own.\n\nIf you specify either `mobile` or `email` to early-version servers you must also supply the\nother so that Command Centre can send both an email invitation and a confirmation SMS.  It\nis an error to only specify one on those servers.\n\nLater versions of Command Centre made the SMS verification optional to better support\ninstallations that use the Gallagher apps but prefer not to depend on cellular connectivity.\nYou do not need to send a mobile number when creating such a credential.\n\nIf you do not give an email address Command Centre cannot send an invitation to the\ncardholder.  It will be up to another application to complete the creation of this\ncredential using the Mobile Connect SDK.\n\nYou should not send a mobile number when creating a credential for use by third-party apps\nthat use the Mobile Connect SDK.  SMS is not supported there.\n\nFor more detail on using the Mobile Connect SDK, including more complete coverage of how\nan email address and mobile number are used in the provisioning process, see its\ndocumentation at [gallaghersecurity.github.io](https://gallaghersecurity.github.io).\n"},"from":{"type":"string","description":"The start of the time period during which this card is active.  If this time is in the\nfuture, the card is not active.\n\nWhen it is not set, Command Centre acts as though it is set to a time in the distant past.\n\nWhen creating a card, leave this property out (or send `null`) to use the card type's\ndefault start time, if the card type has one.  Send an empty string `\"\"` to create the card\nwith no start time even when the card type has a default.  `from` and `until` are treated\nindependently, so leaving out `from` while sending `until` still applies the card type's\ndefault `from`.\n\nWhen modifying a card, send an empty string `\"\"` to reset it.\n\nAny value you send other than a blank string or null must be in RFC 3339 format,\nincluding a timezone.  It must be before midnight on the morning of January 1, 2100 in\nthe server's _local_ time (so servers with positive timezone offsets have maximums\nduring December 31 2099), and it must be less than or equal to the `until` time.  A\nfuture version of Command Centre will clamp too-high values to December 31 2099 and\nreturn a 2xx (success) instead of 400 in that case.\n\nThe server rejects a timestamp that is not in an acceptable format with a 400, whether the\ncard is being created or modified.\n","format":"date-time"},"until":{"type":"string","description":"The end of the time period during which this card is active.  If this time is in the past,\nthe card is not active.\n\nWhen it is not set, Command Centre acts as though it is set to a time in the distant future.\n\nWhen creating a card, leave this property out (or send `null`) to use the card type's\ndefault expiry, if the card type has one: either its fixed end time, or its duration\ncounted from the time the card is created.  Send an empty string `\"\"` to create the card\nwith no end time even when the card type has a default.  `from` and `until` are treated\nindependently, so leaving out `until` while sending `from` still applies the card type's\ndefault `until`.\n\nWhen modifying a card, send an empty string `\"\"` to reset it.\n\nAny value you send other than a blank string or null must be in RFC 3339 format,\nincluding a timezone.  It must be before midnight on the morning of January 1, 2100 in\nthe server's _local_ time (so servers with positive timezone offsets have maximums\nduring December 31 2099), and it must be greater than or equal to the `from` time.  A\nfuture version of Command Centre will clamp too-high values to December 31 2099 and\nreturn a 2xx (success) instead of 400 in that case.\n\nThe server rejects a timestamp that is not in an acceptable format with a 400, whether the\ncard is being created or modified.\n","format":"date-time"},"credentialClass":{"type":"string","description":"This indicates the type of the card.  It comes from an enumeration, and is a reliable way\nof determining the credential's type.\n\nAdded in 8.00.\n\nThe experimental `applePass` was added in 9.10.  Its name may change in future versions.\n\nThe `govPass` credential class will change its name in 9.30.\n","readOnly":true,"enum":["card","digitalId","govPass","mobile","piv","pivi","profileCredential","trackingTag","transact"]},"trace":{"type":"boolean","description":"If set, using this credential will generate an event.\n\nThis field is not in the default set so you will not see it unless you ask for it using\nthe `fields` query parameter.\n\nAdded in 8.30.\n"},"lastPrintedOrEncodedTime":{"type":"string","description":"The date and time this card was last printed or encoded.  It will not come out by\ndefault - you need to ask for it with `fields=cards.lastPrintedOrEncodedTime`.\n\nAdded in 8.40.\n","format":"date-time","readOnly":true},"lastPrintedOrEncodedIssueLevel":{"maximum":15,"minimum":1,"type":"integer","description":"The issue level of this card when it was last printed or encoded, provided it was\nnon-zero.  It will not come out by default - you need to ask for it with\n`fields=cards.lastPrintedOrEncodedIssueLevel`.\n\nAdded in 8.40.\n","readOnly":true},"lastUsedTime":{"description":"When a Gallagher controller last observed the credential in use.\n","type":"string","format":"date-time","readOnly":true},"pin":{"type":"string","description":"This is a write-only field.\nYou can use it in POSTs and PATCHes but the server will not send it to you.\n\nBeing numeric, you might be tempted to send PINs to the server without surrounding\nquotes.  However if you do that, leading zeros will be lost and the resulting behaviour\nis undefined.\n\n8.90 servers will reject your request if the card's `credentialClass` is not `card`,\n`piv`, `pivi`, or `govPass`.  More recent servers will issue a warning in that case, and\nwill not set the PIN, but will allow the rest of the update.\n\nPINs were added to the API in 8.90.\n"},"visitorContractor":{"type":"boolean","description":"This is a credential property only for GovPass, indicating that the enrolled credential is\nfor a visitor / contractor.\n\nIt is a write-once field new to 9.00.\n"},"ownedBySite":{"type":"boolean","description":"This is a credential property only for GovPass, indicating if the site is the owner of the\ncard.\n\nIt is a read-only field new to 9.00.\n","readOnly":true},"credentialId":{"type":"string","description":"Reserved for use by Gallagher applications."},"bleFacilityId":{"type":"string","description":"Reserved for use by Gallagher applications."},"encodingData":{"type":"object","readOnly":true,"properties":{"mifareClassic":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}}},"mifareDesfire":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference"}}}},"description":"This block contains links to the calls that return the data you will need to encode\nMIFARE DESFire and Classic cards.  For more on that see the 'Card Encoding' tag.\n\nRead-only:  the server will ignore it if you send it.\n\nAdded in 9.50.\n\nNot returned by default:  ask for it by adding `cards.encodingData` to your `fields`\nquery parameter.\n"},"deviceType":{"type":"string","enum":["applePhone","appleWatch","androidPhone","androidWatch"],"description":"Required when creating a wallet credential.  Either `applePhone` or `appleWatch` when\ncreating an Apple credential, and either `androidPhone` or `androidWatch` for Google.\n\nWallet credentials are part of the Universal Credentials feature added in 9.60.\n"}},"description":"A _card_ is an access credential, including physical cards and mobile (Bluetooth and NFC)\ncredentials and digital IDs.  The server returns it as part of a [cardholder's\ndetails](models#cardholderdetail), and you supply it when creating or modifying a card\nor credential on a cardholder.\n\nEvery card has a type, a status, and a validity period.  Its type determines other fields of\nrelevance, described below.\n\nPIV cards are complex enough to warrant a [tag of their own](piv).\n"},"CardholderAccessGroup":{"title":"Cardholder access group membership","type":"object","properties":{"href":{"type":"string","description":"This is the href of the cardholder's membership to an access group, not an access group\nitem.\n\nDELETE it to remove this group membership, and use it in the body of a PATCH to a\ncardholder to identify memberships you want to modify.\n\nNote that changing an access group membership with a PATCH will change this href, so do\nnot cache it.\n\nDELETE is the only verb you can use on this URL.  GET returns a 404.\n","format":"uri-reference"},"accessGroup":{"type":"object","description":"An object containing the group's href (which is also a link to its detail page) and,\nwhen sent by the server, the group's name.  You should not send this object when\nmodifying a group membership because you cannot change a group membership's group; you\ncan only change its dates.  But you must send it when adding a new group membership, of\ncourse, because it identifies the cardholder's new group.  Just send the group's href:\ndon't bother with the name.\n\n8.70 and later will not return the link if your operator does not have the privilege to\nview the access group (given by 'View access groups', for example).\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"status":{"type":"object","properties":{"value":{"type":"string","description":"The state of this cardholder's access group membership, in the site's language.  In an\nEnglish locale the value is the same as the type, but capitalised.\n","readOnly":true},"type":{"type":"string","description":"The state of this cardholder's access group membership.\n\nThis will be 'pending' if the activation date is set and in the future,\n'expired' if its deactivation date is set and in the past, or 'active'.\n","readOnly":true,"enum":["pending","expired","active"]}},"description":"The two fields in this block are read-only because they are determined by the `from` and\n`until` dates.\n","readOnly":true},"from":{"type":"string","description":"The start of the time period during which this group membership is active.  If this time\nis in the future, the membership will be inactive.  If null or in the past and `until`\nis null or in the future, the membership will be active.\n\nWhen sending this to a server of version 7.90.883 or earlier, use UTC with a trailing 'Z'.\nMore recent versions understand timezone offsets.\n","format":"date-time"},"until":{"type":"string","description":"The end of the time period during which this group membership is active.  If this time\nis in the past, the membership will be inactive.  If null or in the future and `from` is\nnull or in the past, the membership will be active.\n\nWhen sending this to a server of version 7.90.883 or earlier, use UTC with a trailing 'Z'.\nMore recent versions understand timezone offsets.\n","format":"date-time"}},"description":"An _access group_ is an object in Command Centre.  This section is about a connection between an access group and\na cardholder, called a _membership_.  A cardholder can be a member of many groups, and groups\ncan have any number of members.\n\nLess obvious is that a cardholder can have many memberships to the same group.  This is useful\nbecause a membership has a validity period, expressed with `from` and `until` date-times.\nOutside those moments Command Centre does not regard the cardholder as being a member of the\ngroup.  If there exists one membership with `from` in the past or unset and `until` in the\nfuture or unset, the cardholder is a member.\n\nPresence in an access group affects physical access rights and possession of PDFs, among other\nthings.\n\nThe membership object will come from the server in a cardholder's details if you ask for it, and you send it to\nthe server when modifying a cardholder's group memberships.  All uses are described below.\n"},"CardholderOperatorGroup":{"title":"Cardholder operator group membership","description":"An _operator group_ is an object in Command Centre.  The connection between an operator group\nand a cardholder is a _membership_.  A cardholder can be a member of many different operator\ngroups, and operator groups usually have more than one member, but a cardholder can only have\none membership to a given operator group at a time.  This is because an operator group\nmembership, unlike an access group membership, does not have start and end dates.\n\nPresence in an operator group affects software access.  Your REST operator, for example, must\nbe in an operator group that grants privileges otherwise it will receive nothing but 404s.\n\nAdded to the API in 8.50.\n","type":"object","properties":{"href":{"type":"string","description":"DELETE this URL or use it in the `operatorGroups` block of a [cardholder\nPATCH](cardholders#patch-api-cardholders-id-) to remove this operator group\nmembership.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n","format":"uri-reference"},"operatorGroup":{"type":"object","description":"An object containing a link to this operator group's detail page and its name.\n\nThe link will be absent from the results of a GET if your operator does not have the\nprivilege to view the operator group ('View operators' or 'Edit operators', for\nexample).\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}}}},"CardholderCompetency":{"title":"Cardholder competency","description":"A _competency_ is an object in Command Centre with some basic fields like a name and a notice\nperiod.  Its purpose is to allow a site to refuse access to cardholders who do not meet a\nspecial requirement.\n\nThis section describes the link between a cardholder and a competency.  The status of that link\ndetermines whether they \"hold\" the competency for the purposes of access control decisions at\nthe door.  When we talk about updating, adding, or deleting a competency in the cardholder\nAPI, we do not mean the competency object itself, but rather the link a cardholder has to the\ncompetency.\n\nThe link between cardholder and competency is a block in the cardholder detail.  You can\nalso request the server to add it to the results of a cardholder search by putting\n`competencies` in the `fields` parameter.\n\nWhen updating a cardholder's holding of a competency, the fields you send are slightly\ndifferent from what you receive from a cardholder GET:  you do not send read-only blocks\nsuch as `status` and `competency`, and there is a write-only field `enabled` that you can\nsupply to change the status.\n\nYou cannot use a PATCH to move the status, times, comment, and credit to a different\ncompetency.  If you need to do that, delete the cardholder's existing competency and give\nthem a new one.  You can do that in one PATCH.\n\nA cardholder can have only one link to a given competency.  Attempting to give a cardholder\na competency a second time will either fail or raise a stateful alarm and succeed, depending\non your version of Command Centre.\n","type":"object","properties":{"href":{"type":"string","description":"This is the URL of the cardholder's connection to a competency, not the URL of the\ncompetency itself.  Use it as the target of a DELETE to remove a cardholder's\ncompetency, or in the body of a cardholder PATCH to modify it.\n\nDELETE is the only verb you can use on this URL.  GET will return a 404.\n","format":"uri-reference","readOnly":true},"competency":{"type":"object","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}},"description":"This contains the competency's name and its href.\n\nAs ever, do not bother putting `name` in the body of a POST or PATCH.  The server will\nignore it.\n"},"status":{"type":"object","properties":{"value":{"type":"string"},"type":{"type":"string"}},"description":"This object contains two strings.  Both are read-only, so do not specify them when\nassigning or updating a cardholder's competency.  `value` is taken from the site's\nlanguage pack, suitable for display. `type` comes from a fixed enumeration.  It will be\n`expiryDue` or `active` when the cardholder carries this competency; anything else means\nno.  A fuller explanation follows.\n\nA competency can be disabled, expired, both, or neither.  Whether it is _enabled_ is a\nflag on the cardholder's holding of the competency.  Whether it is _expired_ is derived from an expiry\ntimestamp (accurate to the second):  if it is in the past, Command Centre considers the\ncompetency expired.\n\nA competency can also have an `enablement` date.  If that date (timestamp) passes while the\ncompetency is disabled, Command Centre will enable the competency.  It will leave the date on\nthe item for future reference, though it will not affect the competency again.\n\nIf the competency is disabled, the status `type` will be `inactive` when there is no\nenable date or it is in the past, and `pending` when the enable date is in the future\n(i.e., there is an automatic re-enablement coming).\n\nIf the competency is not disabled, the `expiry` time is important.  If it is in the past,\n`type` will be `expired`.\n\nAll of those cases are negative.  Two remain, when our cardholder is blessed with an\nenabled and active competency.  `type` will be `expiryDue` if the `expires` time is in the\nfuture but within the competency's advance notice period, or `active` if the `expires`\ntime is beyond the advance notice period or not set at all.\n\nYou can see and set the enabled flag, the enable date, and the expiry date via this\nAPI.\n\nWhen creating a cardholder or updating a competency on an existing cardholder, you should\nset the `enabled` field one way or the other, and the `enablement` and `expiry` dates if\nyou wish.  They will determine the contents of this status block.\n\nHere it is in table form.  The first two rows are the positive cases, when Command Centre\nwould grant access to a competency-enforced zone.\n\n| Enabled flag | Enablement date | Expiry date | `status.type`\n| ------------ | -------------- | --------| ----- |\n| true | - | Far future | active |\n| true | - | Near future | expiryDue |\n| true | - | Past | expired |\n| false | Future | - | pending |\n| false | Past | - | inactive |\n| false | Unset | - | inactive |\n","readOnly":true},"disabled":{"type":"boolean","readOnly":true,"deprecated":true,"description":"Do not use this field.  Instead, take the state of a cardholder's competency from\n`status.type`.  This field is misleading on its own:  even when it is true, the\ncompetency might still be inactive.\n"},"enabled":{"type":"boolean","writeOnly":true,"description":"This is a write-only field.  You will not receive it from the GET.  It changes the\ncompetency between enabled (status type `expired`, `expiryDue`, or `active`) and\ndisabled (status type `inactive` or `pending`).  Set it to false in a PATCH if you want\nto disable the competency now, particularly if you are setting a future enablement date.\n"},"expiryWarning":{"type":"string","description":"The time at which the cardholder will (or did) receive a warning about the\ncompetency expiring.  If this time is set and in the past but the competency has\nnot yet expired and is still enabled, the status type will be `expiryDue`.\n","format":"date-time","readOnly":true},"expiry":{"type":"string","description":"The time at which the competency will expire.  If this time is set, in the past, and the\ncompetency is enabled, status type will be `expired`.\n","format":"date-time"},"enablement":{"type":"string","description":"The time at which the competency will be re-enabled.  If set and in the future, and the\ncompetency is disabled, the status type will be `pending`.\n\nYou can set this on an enabled competency but it has no effect there, so with this field\nyou would generally also set `enabled: false`.\n","format":"date-time"},"comment":{"type":"string","description":"The comment appears in the management clients when viewing the cardholder.\n"},"limitedCredit":{"type":"boolean","description":"If false, Command Centre's 'Pre-pay Car Parking' feature (available under its own licence)\nwill not reduce the current credit.\n\nThis field will be in the results if its value is true.\n"},"credit":{"type":"integer","description":"The balance, or amount of credit left on this competency for use by Pre-pay Car Parking.\nIt can be negative.\n\nThis field will be in the results if its value is not zero.\n"}}},"CardholderRelationship":{"title":"Cardholder relationship","description":"A _role_ is an object in Command Centre.  Roles are usually named using nouns such as\n'supervisor', 'manager', or 'team leader'.\n\nThe operator clients and the REST API allow you to create a link between two\ncardholders, called a relationship, using a role.  The link is directional:  we\nrefer to the cardholder who 'has' the role as the child, and the cardholder who\nperforms or 'is' the role as the parent.\n\nA parent can perform a role for any number of child cardholders, but a child can only have one\nrelationship (parent) for each role.  Command Centre will reject your submission if you try to\ncreate a relationship when one already exists for the same child and role.\n\nFor example, a role on your system might be 'supervisor'.  A cardholder can be a supervisor\nfor any number of others, but will only have one supervisor.\n\nLoops are possible:  two cardholders can supervise each other.\n\nThe REST API manages relationships through the child cardholder.  You can create them at the\nsame time as creating the child in a POST, or add them later in a PATCH.\n\nThere is currently no way to list all a cardholder's children in one request (everyone a\nparticular cardholder is supervising, for example).  You would achieve that by iterating\nthrough all cardholders, after re-reading the efficiency tips, and checking their\nrelationships - quite easily done in JSONPath.\n\nThis section describes the object you receive in a cardholder's detail page and you will send\nin a POST or PATCH.  The child cardholder does not appear in it because he or she is\nidentified by the URL of the request.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"This is the href of the link between two cardholders.  DELETE it, or put it in\n`relationships.remove.href` of a PATCH directed at the child cardholder, to sever the\nrelationship.  Specify it in `relationships.update.href` of a PATCH to the child\ncardholder to update the relationship.\n\nDo not specify it when creating a new relationship.  The server generates it for you.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n"},"role":{"type":"object","description":"This is the role that the parent identified in the `cardholder` block performs for the\ncardholder identified by the request URL.\n\nOnce set, this cannot be changed.  Command Centre will ignore it if you send it\nin an update PATCH.  If you need to swap a parent from one role to another, send\nan add and a delete in the same PATCH.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"cardholder":{"type":"object","description":"The href and name of the cardholder that performs this role.\n\nThe three name fields are read-only:  Command Centre sends them to you in the body of a\nGET but will ignore them if you send them in the body of a POST or PATCH.\n\nThis block and the href in it are unnecessary when deleting a relationship.  The href is\nrequired when creating one, and optional when updating, but strongly advised since the\ncardholder is the only thing about a relationship you can change.\n\n`firstName` and `lastName` appeared in 8.20.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for the parent cardholder."},"firstName":{"type":"string","readOnly":true},"lastName":{"type":"string","readOnly":true}}}}},"CardholderLocker":{"title":"Cardholder locker","description":"These appear in an array in a cardholder detail, showing the cardholder's allocated lockers.\nEach allocation has `from` and `until` dates, much like cards and access group memberships,\noutside of which the allocation is inactive.\n\nA locker can have allocations to more than one cardholder, with separate or overlapping\nperiods.  Unlike access groups, however, one cardholder cannot have more than one allocation\nto the same locker.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"DELETE this to end a cardholder's use of a locker, or use it to identify the\nallocation you wish to modify in a [cardholder\nPATCH](cardholders#patch-api-cardholders-id-).\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n"},"locker":{"description":"The href in this object is a link to the allocated locker, and is the identifier to use\nwhen allocating the same locker to another cardholder.\n\nThe object also contains the name and short name of the locker, and the name and\nidentifying href of its bank.\n","type":"object","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for a locker."},"shortName":{"type":"string","readOnly":true},"lockerBank":{"type":"object","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}}}},"from":{"description":"The start of the time period during which the cardholder has access to this locker.\n\nSend an empty string `\"\"` to reset it.\n","type":"string","format":"date-time"},"until":{"description":"The end of the time period during which the cardholder has access to this locker.\n\nSend an empty string `\"\"` to reset it, making the allocation permanent.\n","type":"string","format":"date-time"}}},"CardholderElevatorGroup":{"title":"Cardholder elevator group","description":"These appear in an array in a cardholder detail, showing the cardholder's elevator group\nproperties, and can go into the body of a POST or PATCH to set a cardholder's elevator group\nproperties.\n\nA cardholder can have one default floor per elevator group. The elevator system will\nprepare a car to carry that cardholder to the access zone shown here when the cardholder\nbadges a card at an appropriately configured kiosk.\n\nElevator system features can be activated by enabling the feature for an elevator group.\nFor example, 'VIP features' gives exclusive access to an elevator car to the passenger.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"The href of this cardholder's elevator group entry.  DELETE this to remove it from the\ncardholder.\n"},"elevatorGroup":{"type":"object","description":"The href and name of the elevator group for which this cardholder has a default\nfloor or passenger type.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"accessZone":{"type":"object","description":"The href and name of the access zone (floor) to which this cardholder is most\nlikely to want to travel after entering the group's main elevator lobby. This \nproperty will be missing if the cardholder does not have a default floor for this elevator group.\n","properties":{"name":{"type":"string","readOnly":true},"href":{"type":"string","format":"uri-reference","description":"The API's identifier for an item."}}},"enableCaptureFeatures":{"type":"boolean","description":"Cardholders can select and recall specific elevators to specific floors using\na kiosk. Once captured, the elevator car can be placed on independent service to\ngive users control of the car to clean the interior or perform maintenance.\n"},"enableCodeBlueFeatures":{"type":"boolean","description":"A special elevator mode which is commonly found in hospitals. It allows an elevator\nto be summoned to any floor for use in an emergency situation.\n"},"enableExpressFeatures":{"type":"boolean","description":"Allows the cardholder to program selected elevators to cycle continuously between two\nfloors for a pre-determined duration. For example, this feature can help hotels transport\nfood efficiently from their kitchen to a ballroom on another floor. You can also prevent\nother guests from boarding to provide your banquet guests with VIP treatment.\n"},"enableServiceFeatures":{"type":"boolean","description":"Service personnel can use this function to call an empty elevator and ride it nonstop\nto their destination floor. The user simply registers a call via a card swipe or PIN entry\nthat is pre-programmed to grant access.\n"},"enableService2Features":{"type":"boolean","description":"Service personnel can use this function to call an empty elevator and ride it nonstop\nto their destination floor. The user simply registers a call via a card swipe or PIN entry\nthat is pre-programmed to grant access.\n"},"enableService3Features":{"type":"boolean","description":"Service personnel can use this function to call an empty elevator and ride it nonstop to\ntheir destination floor. The user simply registers a call via a card swipe or PIN entry\nthat is pre-programmed to grant access.\n"},"enableVipFeatures":{"type":"boolean","description":"VIP operation allows cardholders to swipe a card or enter a PIN to isolate the elevator\nand provide uninterrupted access to their designated floor.\n"}}},"CardholderRedaction":{"title":"Cardholder redaction","description":"These appear in an array in a cardholder object.  Each describes a redaction scheduled for the\ncardholder.\n\nA cardholder can have multiple event redactions pending, because they can operate on events\nfrom different periods, but since a cardholder redaction removes the cardholder item there\ncan be only one.\n","properties":{"href":{"type":"string","format":"uri-reference","description":"DELETE this URL to cancel this redaction.\n\nDELETE is the only verb you can use on this URL.  GET will always return a 404.\n"},"type":{"type":"string","enum":["normalEvents","cardholder"],"description":"Whether this redaction is for cardholder events or cardholder information.\n"},"when":{"description":"When the redaction is meant to happen.  This should be in the future.  If it is in the past,\nthe service returns 400-Bad Request Invalid Start Time.\n\nOptional.  If it is absent, it means to do it asap.\n","type":"string","format":"date-time"},"before":{"description":"For event redactions, do not redact any events after this time.  No effect on cardholder\ninformation redactions.\n\nOptional.\n","type":"string","format":"date-time"},"status":{"type":"string","enum":["pending","inProgress","cancelled","done","failed"],"description":"The status of this redaction.\n"},"redactionOperator":{"description":"A block containing the href and current name of the operator who scheduled the redaction.\n","type":"object"}}}}},"paths":{"/api/cardholders/{id}":{"get":{"tags":["Cardholders"],"summary":"Get details of a cardholder","description":"This retrieves one cardholder from Command Centre.  It does not return all the fields\navailable (there are too many) so, much like the cardholder search, it accepts a `fields`\nparameter where you specify the fields you need.\n\n[Do not use](forward-compatibility-hateoas) a\ncardholder's ID to build the URL yourself:  follow the href in the [cardholder\nsearch](models#cardholders) to get here.\n","parameters":[{"name":"fields","in":"query","description":"Specifies the fields you want in the results.  The values you can list are the same as the\nfield names in the [detail results](models#cardholderdetail).  Use it to return\nfewer fields than normal.  Separate values with commas.\n\nTreat the string matches as case sensitive:  use 'lastName' rather than 'lastname'.\n\nThe `personalDataFields` field (short form `pdfs`) returns only the cardholder's non-empty\npersonal data.  That makes it difficult to find out which PDFs a cardholder _could_ have,\nso the special field `allPdfs` returns all of them, including those for which the\ncardholder has no value.\n\nAdded to the cardholders controller in 8.00.  In that version you will receive the href,\ninternal ID, and updates link even if you do not ask for them.  In 8.10 you will not.  If\nyou are going to send the fields parameter and need those fields, include them.\n","style":"form","explode":false,"schema":{"type":"array","items":{"type":"string"},"default":["defaults"]}}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardholderDetail"}}}},"404":{"description":"That is not the URL of a cardholder, or the operator does not have the privilege to view\nthat cardholder.\n"}}}}}}
```

## Remove a cardholder

> This call removes a cardholder from Command Centre.  You can find the URL in the href in the\
> \[cardholder search]\(models#cardholders) or any of the other calls that return\
> cardholder hrefs.  \[Do not build it yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}":{"delete":{"tags":["Cardholders"],"summary":"Remove a cardholder","description":"This call removes a cardholder from Command Centre.  You can find the URL in the href in the\n[cardholder search](models#cardholders) or any of the other calls that return\ncardholder hrefs.  [Do not build it yourself](forward-compatibility-hateoas).\n","responses":{"200":{"description":"Success."},"204":{"description":"Success."},"400":{"description":"Deleting the cardholder failed.  This happens when the cardholder is a critical part of\nanother construct (a personalised notification, for example).\n"},"403":{"description":"The operator does not have permissions to delete that cardholder\nor the server is not licensed for cardholder operations.\n\nIn versions up to and including 9.20 403 also means that there is no such cardholder.\n"},"404":{"description":"New in 9.30.  There is no such cardholder.\n"},"409":{"description":"The cardholder is locked for editing by another operator.  The body of the response will\ntell you which operator is holding the lock.\n"}}}}}}
```

## Update a cardholder

> This is the call you use to update a cardholder, including:\
> &#x20; \- changing general properties such as names, division, and description,\
> \
> &#x20; \- changing PDF values, and\
> \
> &#x20; \- adding or updating cards, access group membership, roles and relationships, lockers, and\
> &#x20;   competencies.\
> \
> When changing a cardholder's division at the same time as PDFs or competencies, the extra\
> privilege checks required for PDFs and competency changes use the origin division, not the\
> destination division.  So if you are moving a cardholder from a division in which your\
> operator has no access to PDFs and competencies into one in which it does, first PATCH the\
> cardholder into the new division then PATCH it with the other changes.\
> \
> You can find this URL in the href in the \[cardholder search]\(models#cardholders) or\
> any of the other calls that return cardholder hrefs.  \[Do not build it\
> yourself]\(forward-compatibility-hateoas).\
> \
> \---\
> &#x20; \
> Note that the REST API does not implement the full suite of Command Centre\
> privileges.  In particular, the following privileges do not have the same effect\
> on an operator's ability to modify a cardholder that they do in the administrative\
> clients:\
> \
> &#x20; \- Disable Card.  This privilege has no effect on the 7.90 REST API.  You need\
> &#x20;   Edit Cardholders to disable cards.\
> \
> &#x20; \- Add or Edit Cardholder Notes.  You also need Edit Cardholders to change notes\
> &#x20;   on an existing cardholder via the API.  In the administrative clients, you do\
> &#x20;   not.\
> \
> &#x20; \- Manage Locker Assignments.  You also need Edit Cardholders to assign and un-assign\
> &#x20;   lockers on a cardholder via the API.  In the administrative clients, you do not.\
> \
> The 'De-authorise Cardholder' privilege \_is\_ implemented.  It allows an operator to set a\
> cardholder's \`authorised\` field to false (denying all their future access requests) without\
> the Edit Cardholders privilege.\
> \
> In short, if your application intends to do more to cardholders than de-authorise them, you\
> will need an operator with Edit Cardholders.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"schemas":{"CardholderPATCH":{"title":"Cardholder PATCH","description":"Send one of these in a PATCH to `/api/cardholders/{id}` to modify the cardholder at that\nURL, or to add and modify cards, competencies, personal data, group memberships, and\nrelationships.\n\nThis model only shows some of the fields available on a cardholder.  The full list is in\nthe [cardholder detail](models#cardholderdetail).\n","properties":{"authorised":{"type":"boolean"},"firstName":{"type":"string"},"@employeeId":{"type":"string","description":"Replace PDF values as though they were flat fields on a cardholder.  Prefix the name of\nthe PDF with `@`, and Base64-encode images.  Send 'null' if you want to delete a PDF value.\n\nRemember that a cardholder's record will not return a PDF if they are not a member of an\naccess group that grants that PDF.  Current versions of Command Centre are tolerant of\nyour attempts to change the PDF in that situation, but future versions may not be.\n"},"personalDataDefinitions":{"description":"This is how you set and unset the notifications flags on PDFs.\n","type":"array","items":{"$ref":"#/components/schemas/CardholderPDF"}},"cards":{"type":"object","description":"This object can contain three arrays, named `add`, `update`, and `remove`.  Every element\nyou put in those arrays should be in the [card schema](models#cardholdercard).\n\nEach element of the `add` array will need a `type` member, at the very least.  Every\ncard field makes sense here except `href`.  Only existing cards have hrefs.  This\nexample adds two cards:  one has nothing more than the type, so it will receive the card\ntype's default validity dates, if any, and a computed number and issue level.  The other\nis a mobile credential (it has an `invitation` block) with a custom initial state.\n\nEach element of the `update` array should be a card to modify.  It will need the href of\nthat card, plus the fields you want to change.  Remember you cannot change a card's type.\nThe example changes the issue level and resets the `until` date.\n\nThe only fields that make sense in an element of the `remove` array are `href` and `status`.\n\nYou can remove and add cards in the same PATCH.  In fact you should do that in preference\nto making multiple API calls.  That is a good way of reissuing a mobile credential, for\nexample:  put the href to the old one in the `remove` array and a new invitation in the\n`add` array.  The new credential should have the same card number and the same `type` and\n`invitation` blocks as the credential you're re-issuing.\n\nDo not put the same href in both the `update` and `remove` arrays.\n\nIn version 8.90 and later you can specify a card state in the `value` field inside the\n`status` block when removing a card or changing its issue level.  It becomes the final\nstate of the card if you remove it or the final state of the card with the previous issue\nlevel if you re-issue it, so it must be one of the valid states for the card type.  The\nGallagher clients and the resulting event call this state the 'reason' for the re-issue or\nremoval.  By default it will be the same as the card's current state.\n"},"accessGroups":{"type":"object","description":"Like the `cards` object, this can contain three arrays named `add`, `update`, and\n`remove`.  Every element you send in those arrays should be in the [access group\nschema](models#cardholderaccessgroup).\n\nThis operation does not modify access groups in Command Centre; it works on a cardholder's\nmemberships to those groups.\n\nEach element of the `add` array will need an `accessGroup` member containing an href\nidentifying the group to which you wish to add the cardholder.  The other fields are\noptional.  If you omit `from`, the membership will take effect immediately.  If you omit\n`until`, it will be unending.\n\nEach element of the `update` array will need an href identifying the membership (not the group!) to update,\nand one or both of the `from` and `until` date-times with new values.  You cannot\nchange the group:  just the activity period.  The example removes the until date, effectively making\nthe group membership unending.\n\nIn version 7.90.883 or earlier, `from` and `until` should be in UTC with a\ntrailing 'Z'.  Releases after 883 understand different timezones here.\n\nNote that updating a cardholder's group membership will change its href, so do not cache\nit.\n\nThe only field that makes sense in an element of the `remove` array is the href.\n\nDo not put the same href in both the `update` and `remove` arrays.\n"},"competencies":{"type":"object","description":"Like the cards and accessGroups objects, this can contain three arrays named `add`,\n`update`, and `remove`.  Every element should be in the [cardholder competency\nschema](models#cardholdercompetency).\n\nThis operation does not modify Command Centre's competencies:  it works on a cardholder's\nholdings of those competencies.\n\nEach element of the `add` array will need a `competency` block containing an href member\nidentifying the competency you wish to grant the cardholder.  All other fields are optional.\nNote that attempting to give a cardholder a competency he or she already has\nwill result in an error or an alarm depending on the server version.\n\nEach element of the `update` array will need an href identifying the\ncardholder/competency link to update, and one or more of the `expiry`, `enabled`,\n`enablement`, `comment`, `limitedCredit`, and `credit` fields.\n\nThe only field that makes sense in an element of the `remove` array is the href\nof the link between the cardholder and the competency.\n\nDo not put the same href in both the `update` and `remove` arrays.\n\nIn this example we are giving the cardholder a disabled competency which will enable\nin 2021, and activating another competency.\n"},"relationships":{"type":"object","description":"It should be no surprise that this can contain three arrays named `add`, `update`, and\n`remove`, and that every element should be in the [relationship\nschema](models#cardholderrelationship).\n\nThis operation does not modify Command Centre's roles:  it works on the relationships\nbetween two cardholders.\n\nEach element of the `add` array will need a `role` member containing an href identifying\nthe type of relationship you wish to establish, and a `cardholder` member containing an\nhref identifying the other party (the one who will perform the role for the cardholder at\nthe URL you are PATCHing).  There are no optional fields.\n\nEach element of the `update` array will need an href identifying the relationship to\nupdate, and one or both of the `role` and `cardholder` blocks containing the updated\nvalues.  The example leaves the role but changes the cardholder - a new supervisor,\npresumably.\n\nThe only field that makes sense in an element of the `remove` array is the href.\n\nDo not put the same href in both the `update` and `remove` arrays.\n"},"lockers":{"type":"object","description":"With you well in the habit by now, each element of your three arrays should be in the\n[cardholder locker schema](models#cardholderlocker).  They will allocate lockers to\ncardholders, de-allocate them, and adjust validity periods.\n\nEach member of the `add` array will need a `locker` member containing an href identifying\nthe locker to allocate.  The cardholder you will allocate it to is identified by the\nrequest URL, remember.\n\nEach member of the `update` array will need an href identifying the allocation to update,\nand one or both of the `from` and `until` date-times.\n\nThe validity period is all you can change about a locker allocation.  If you want to\nchange the locker, delete the old one and add another.  You can do that in the same PATCH,\nwith one element in each of the `remove` and `add` arrays.\n\nThis example allocates one locker starting in January 2019, sets the end-date of an\nexisting allocation to the end of February 2020, and removes another entirely.\n"},"operatorGroups":{"type":"object","description":"This can contain two arrays named `add` and `remove`.  Every element you send in those\narrays should be in the [cardholder operator group\nschema](models#cardholderoperatorgroup).\n\nEach element of the `add` array will need an `operatorGroup` member containing an href\nidentifying the operator group to which you wish to add the cardholder.  Supported in 8.50\nand later.\n\nThe only field that makes sense in an element of the `remove` array is the href.  Make\nsure it is the href of the membership, not of the operator group.  Supported in 8.90 and\nlater.\n\nThere is nothing about an operator group membership that you can change so there is no\npoint to an `update` array.  Operator group memberships are, or are not:  there is no\nupdate.\n"},"elevatorGroups":{"type":"object","description":"This can contain three arrays named `add`, `update`, and\n`remove`, each containing an element in the [cardholder elevator group\nschema](models#cardholderelevatorgroup).\n\nThe `elevatorGroup` block only makes sense in the `add` array.  The `update` array is for\nchanging the cardholder's default floor and passenger types on an existing elevator group\nassignment.\n\nThe server will ignore all fields in the elevatorGroup and accessZone objects except `href`\nif you place them in the body of your PATCH.\n\nRemove the default floor in an update by supplying an accessZone block with the href set\nto null or \"\".\n\nChange a passenger type in an update by supplying the new value. The passenger type will\nbe unchanged otherwise.\n\nAs with cards and access group memberships etc., the server ignores root-level hrefs in\nthe elements of an `add` array, requires them in the `update` array (since they indicate\nthe entries to work on), and ignores everything but them in the `remove` array.\n\nThe example shows our cardholder receiving a default floor for one elevator group and\nupdating the Code Blue and VIP passenger types for another elevator group.\n\nAdded in 8.50.\n"}}},"CardholderPDF":{"title":"Cardholder PDF","description":"A personal data definition and its value for a cardholder.  Each definition is an object\ncontaining one property named after the PDF (plus a leading '@'), which in turn contains its\nvalue and notifications flag for the containing cardholder and an object containing some of\nthe PDF definition's basic fields.\n\nThis is returned as part of a [cardholder](models#cardholderdetail), and you can send it\nin a [POST](models#cardholderpost) or a\n[PATCH](models#cardholderpatch) to set the PDF's value and notification flag for\na cardholder.  The server will ignore the `definition` block if you send it back.  It uses the\nname of the block (minus its leading `@`) to find the PDF to change.\n\nWhile you can use this structure to change the value of a cardholder's PDF, it may be simpler\nto put a property in the root of the payload named after the PDF with a leading '@'.\n","type":"object","additionalProperties":{"$ref":"#/components/schemas/CardholderPDFContent"}},"CardholderPDFContent":{"title":"Cardholder PDF content","type":"object","properties":{"href":{"type":"string","description":"GET this link to receive this cardholder's value for this PDF.  If it is an image you will\nreceive the raw image data with an appropriate content-type.  A browser will render it\ncorrectly.\n\nIf the PDF is not an image the content type will be text/plain, encoded (like\neverything else in this API) as UTF-8.  But you should only use it for image PDFs\nbecause:\n\n* Values for scalar PDF types (everything except images) are also given to you in the\n  `value` field in this object, described below.\n\n* The format of date PDFs varies with the Command Centre version.  Old servers return\n  it in a format that depends on the server's locale.  This was a bug.  Newer servers\n  use ISO 8601.\n\n* If you learn this URL while a cardholder has the PDF, then the cardholder loses\n  it, then you GET the URL, results will vary depending on the server version.  The\n  correct result is a 404, and if the PDF is an image or the server is recent you\n  will see that.  But old servers may return a blank string or even a 500 for\n  non-image PDFs.\n\nThis field will be absent if the cardholder does not have a value for the PDF.\n\nIt is read-only:  do not send it in a PATCH or POST.\n","format":"uri-reference","readOnly":true},"definition":{"type":"object","properties":{"href":{"type":"string","description":"This is the href of the PDF's definition, common to all cardholders who hold a value\nfor this PDF.\n","format":"uri-reference"},"name":{"type":"string","description":"The PDF's name, without an '@' prefix.\n"},"id":{"type":"string","description":"Short alphanumeric identifier used elsewhere in the API to filter cardholder searches\nand add PDF values to alarm and event GETs.\n"},"type":{"type":"string","enum":["string","image","strEnum","numeric","date","address","phone","email","mobile"]}},"description":"The definition object is read-only:  do not send it in a PATCH or POST.\n","readOnly":true},"value":{"description":"In a GET response for text, numeric, and date PDFs this will be a scalar containing the\nvalue, but for image PDFs it will be an object containing a copy of the `href` field\nabove.  For date PDFs the string will comply with RFC 3339, but may or may not contain a\ntime depending on the version of Command Centre.  Newer versions omit the `T` separator\nand everything after it.  If there, it will be midnight UTC.\n\nIn versions up to and including 8.60, if a cardholder did not have an image PDF captured\n(in other words they were a member of an access group that included an image PDF, but they\ndid not have a value for it) the API returned the string 'Not captured' in the `value`\nfield.  Versions 8.70 and later will not return the `value` field at all if there is no image.\n\nIn a POST or PATCH send text PDFs (including dates) as strings, numeric PDFs as numbers or\nstrings that parse to numbers, and image data Base64-encoded into a string.\n","oneOf":[{"type":"integer","title":"Numeric","description":"The scalar value of the numeric PDF."},{"type":"string","title":"Text or date","description":"The scalar value of the text or date PDF."},{"type":"object","title":"Image","description":"An object containing an href which, when requested from the server, returns the\nimage PDF.  The server does not transcode images:  it will arrive as the same\ncontent type as uploaded.\n","properties":{"href":{"type":"string","format":"uri-reference","readOnly":true,"description":"Only present for image PDFs, this will be a copy of the href field in the parent\nobject.  When requested from the server it will return the image PDF as it was\nuploaded.\n"}}}]},"notifications":{"type":"boolean","description":"In a GET response, this will only appear for email and mobile PDF types.  In a POST or\nPATCH, it only makes sense for the same types.\n\nIf true, cardholder notifications will go to the telephone number or email address held by\nthis PDF.  Cardholders can have their notifications go to as many contacts as they wish.\n"}}}},"responses":{"409ch":{"description":"The cardholder is locked for editing by another operator.  The body of the response\nwill tell you which operator is holding the lock.\n\nA 409 may also mean that the operator does not have the privilege to modify that\ncardholder, or you attempted to set a field for which you have no privilege (such as\n'notes' or 'operatorPassword', both of which require special privileges).  The server\nreturns a 409 in these cases for compatibility with some versions of iOS applications.\nIn versions prior to 8.80 your operator needed 'Edit cardholders' to de-authorise a\ncardholder.  In 8.80 and later, 'De-authorise cardholder' on its own is enough.\n"}}},"paths":{"/api/cardholders/{id}":{"patch":{"tags":["Cardholders"],"summary":"Update a cardholder","description":"This is the call you use to update a cardholder, including:\n  - changing general properties such as names, division, and description,\n\n  - changing PDF values, and\n\n  - adding or updating cards, access group membership, roles and relationships, lockers, and\n    competencies.\n\nWhen changing a cardholder's division at the same time as PDFs or competencies, the extra\nprivilege checks required for PDFs and competency changes use the origin division, not the\ndestination division.  So if you are moving a cardholder from a division in which your\noperator has no access to PDFs and competencies into one in which it does, first PATCH the\ncardholder into the new division then PATCH it with the other changes.\n\nYou can find this URL in the href in the [cardholder search](models#cardholders) or\nany of the other calls that return cardholder hrefs.  [Do not build it\nyourself](forward-compatibility-hateoas).\n\n---\n  \nNote that the REST API does not implement the full suite of Command Centre\nprivileges.  In particular, the following privileges do not have the same effect\non an operator's ability to modify a cardholder that they do in the administrative\nclients:\n\n  - Disable Card.  This privilege has no effect on the 7.90 REST API.  You need\n    Edit Cardholders to disable cards.\n\n  - Add or Edit Cardholder Notes.  You also need Edit Cardholders to change notes\n    on an existing cardholder via the API.  In the administrative clients, you do\n    not.\n\n  - Manage Locker Assignments.  You also need Edit Cardholders to assign and un-assign\n    lockers on a cardholder via the API.  In the administrative clients, you do not.\n\nThe 'De-authorise Cardholder' privilege _is_ implemented.  It allows an operator to set a\ncardholder's `authorised` field to false (denying all their future access requests) without\nthe Edit Cardholders privilege.\n\nIn short, if your application intends to do more to cardholders than de-authorise them, you\nwill need an operator with Edit Cardholders.\n","requestBody":{"description":"As well as cardholder attributes such as `authorised`, the PATCH body contains\ninstructions for creating, updating, and deleting personal data, group\nmemberships, etc.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardholderPATCH"}}},"required":true},"responses":{"200":{"description":"Success.  Future versions will add feedback from the server about your PATCH.\n"},"204":{"description":"Success, with no feedback."},"400":{"description":"The parameters are invalid, or other errors prevented the update.\n\nIf you receive 'No fields have been defined for update', check that your submission body\nis valid JSON.\n"},"403":{"description":"The site does not have a RESTCardholders licence, or you attempted to set a PDF for\nwhich you have no privilege.\n"},"409":{"$ref":"#/components/responses/409ch"}}}}}}
```

## Remove an access group membership

> This call removes a cardholder's membership in an access group.  Note that a cardholder may\
> have more than one membership in a group.\
> \
> You can find this URL in the \[cardholder object]\(models#cardholderdetail).  \[Do not\
> build it yourself]\(forward-compatibility-hateoas).\
> \
> To modify a cardholder's membership of an access group your operator must have a\
> privilege that allows editing that cardholder plus 'Modify access control' on the\
> group.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"responses":{"409ch":{"description":"The cardholder is locked for editing by another operator.  The body of the response\nwill tell you which operator is holding the lock.\n\nA 409 may also mean that the operator does not have the privilege to modify that\ncardholder, or you attempted to set a field for which you have no privilege (such as\n'notes' or 'operatorPassword', both of which require special privileges).  The server\nreturns a 409 in these cases for compatibility with some versions of iOS applications.\nIn versions prior to 8.80 your operator needed 'Edit cardholders' to de-authorise a\ncardholder.  In 8.80 and later, 'De-authorise cardholder' on its own is enough.\n"}}},"paths":{"/api/cardholders/{id}/access_groups/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove an access group membership","description":"This call removes a cardholder's membership in an access group.  Note that a cardholder may\nhave more than one membership in a group.\n\nYou can find this URL in the [cardholder object](models#cardholderdetail).  [Do not\nbuild it yourself](forward-compatibility-hateoas).\n\nTo modify a cardholder's membership of an access group your operator must have a\nprivilege that allows editing that cardholder plus 'Modify access control' on the\ngroup.\n","responses":{"204":{"description":"Success."},"400":{"description":"The operator does not have sufficient privilege."},"403":{"description":"The operator does not have sufficient privilege."},"404":{"description":"That is not the href of an access group membership."},"409":{"$ref":"#/components/responses/409ch"}}}}}}
```

## Remove a card from a cardholder

> This call removes a card from a cardholder.\
> \
> You can find this URL in the \[cardholder object]\(models#cardholderdetail).  \[Do not\
> build it yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"responses":{"409ch":{"description":"The cardholder is locked for editing by another operator.  The body of the response\nwill tell you which operator is holding the lock.\n\nA 409 may also mean that the operator does not have the privilege to modify that\ncardholder, or you attempted to set a field for which you have no privilege (such as\n'notes' or 'operatorPassword', both of which require special privileges).  The server\nreturns a 409 in these cases for compatibility with some versions of iOS applications.\nIn versions prior to 8.80 your operator needed 'Edit cardholders' to de-authorise a\ncardholder.  In 8.80 and later, 'De-authorise cardholder' on its own is enough.\n"}}},"paths":{"/api/cardholders/{id}/cards/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove a card from a cardholder","description":"This call removes a card from a cardholder.\n\nYou can find this URL in the [cardholder object](models#cardholderdetail).  [Do not\nbuild it yourself](forward-compatibility-hateoas).\n","responses":{"200":{"description":"Success."},"204":{"description":"Success."},"403":{"description":"The operator does not have a privilege that allows editing that cardholder."},"404":{"description":"That card is not on that cardholder."},"409":{"$ref":"#/components/responses/409ch"}}}}}}
```

## Remove a competency from a cardholder

> This call removes a competency from a cardholder.\
> \
> You can find this URL in the \[cardholder object]\(models#cardholderdetail).  \[Do not\
> build it yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/competencies/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove a competency from a cardholder","description":"This call removes a competency from a cardholder.\n\nYou can find this URL in the [cardholder object](models#cardholderdetail).  [Do not\nbuild it yourself](forward-compatibility-hateoas).\n","responses":{"200":{"description":"Success."},"204":{"description":"Success."},"403":{"description":"The operator does not have a privilege that allows editing that cardholder."},"404":{"description":"That is not the href of a cardholder's competency."}}}}}}
```

## Change competency credit

> This call increases or decreases a cardholder's competency credit.  It is an indivisible\
> operation.\
> \
> It is reserved for the Pre-pay Car Parking feature.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/competencies/{secondary_id}/credit":{"post":{"tags":["Cardholders"],"summary":"Change competency credit","description":"This call increases or decreases a cardholder's competency credit.  It is an indivisible\noperation.\n\nIt is reserved for the Pre-pay Car Parking feature.\n","parameters":[{"name":"add","in":"query","required":true,"schema":{"type":"integer"},"description":"The amount to adjust the competency credit.  This can be positive or negative."}],"responses":{"200":{"description":"Success.  Future versions will return feedback from the server."},"204":{"description":"Success."},"403":{"description":"The operator does not have a privilege that allows editing that cardholder."},"404":{"description":"The parameters are invalid."}}}}}}
```

## Remove an elevator group from a cardholder

> This call removes a default floor assignment and passenger types from a cardholder for one\
> elevator group.\
> \
> You will find this URL in the \`elevatorGroups\` block of a \[cardholder\
> object]\(models#cardholderdetail).  \[Do not build it\
> yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/elevator_groups/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove an elevator group from a cardholder","description":"This call removes a default floor assignment and passenger types from a cardholder for one\nelevator group.\n\nYou will find this URL in the `elevatorGroups` block of a [cardholder\nobject](models#cardholderdetail).  [Do not build it\nyourself](forward-compatibility-hateoas).\n","responses":{"204":{"description":"Success."},"403":{"description":"Your operator does not have the necessary privilege to change this cardholder's elevator\ngroups.\n"},"404":{"description":"That is not the URL of a cardholder's elevator group.  You can take an href\nfrom the `elevatorGroups` block of a cardholder detail.  The message in the results\ndocument will tell you more about the problem.\n"}}}}}}
```

## Remove a locker assignment

> This call removes a locker assignment from a cardholder.  If the cardholder has no other\
> assignments for this locker after this operation he or she will not be able to open it.\
> \
> You will find this URL in the \`lockers\` block of a \[cardholder\
> object]\(models#cardholderdetail).  In the interest of compability with future versions of Command Centre, \[do not\
> build it yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/lockers/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove a locker assignment","description":"This call removes a locker assignment from a cardholder.  If the cardholder has no other\nassignments for this locker after this operation he or she will not be able to open it.\n\nYou will find this URL in the `lockers` block of a [cardholder\nobject](models#cardholderdetail).  In the interest of compability with future versions of Command Centre, [do not\nbuild it yourself](forward-compatibility-hateoas).\n","responses":{"204":{"description":"Success."},"400":{"description":"You do not have privileges for the operation."},"403":{"description":"You do not have privileges for the operation."},"404":{"description":"That is not the URL of a cardholder's locker assignment.  You can take an href from the\n'lockers' block of a cardholder detail, or the 'assignments' block of a locker or locker\nbank detail.  The message in the results document will tell you more about the problem.\n"},"409":{"description":"You do not have privileges for the operation."}}}}}}
```

## Remove an operator group membership

> This call removes a cardholder's membership in an operator group.  Operator group\
> memberships do not have start and end dates, so a cardholder can only have one membership in\
> a given operator group.\
> \
> You can find this URL in the \[cardholder object]\(models#cardholderdetail).  In the\
> interest of forward compability, \[do not build it\
> yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}},"responses":{"409ch":{"description":"The cardholder is locked for editing by another operator.  The body of the response\nwill tell you which operator is holding the lock.\n\nA 409 may also mean that the operator does not have the privilege to modify that\ncardholder, or you attempted to set a field for which you have no privilege (such as\n'notes' or 'operatorPassword', both of which require special privileges).  The server\nreturns a 409 in these cases for compatibility with some versions of iOS applications.\nIn versions prior to 8.80 your operator needed 'Edit cardholders' to de-authorise a\ncardholder.  In 8.80 and later, 'De-authorise cardholder' on its own is enough.\n"}}},"paths":{"/api/cardholders/{id}/operator_groups/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove an operator group membership","description":"This call removes a cardholder's membership in an operator group.  Operator group\nmemberships do not have start and end dates, so a cardholder can only have one membership in\na given operator group.\n\nYou can find this URL in the [cardholder object](models#cardholderdetail).  In the\ninterest of forward compability, [do not build it\nyourself](forward-compatibility-hateoas).\n","responses":{"204":{"description":"Success."},"403":{"description":"The operator does not have a privilege that allows editing that cardholder's operator\ngroup memberships ('Modify operator group membership').\n"},"404":{"description":"That is not the href of an operator group membership."},"409":{"$ref":"#/components/responses/409ch"}}}}}}
```

## Remove a relationship

> This call severs a relationship between two cardholders.\
> \
> You can find the URL in the \`relationships\` block in the \[cardholder\
> object]\(models#cardholderdetail) of the cardholder who has the relationship, not the\
> cardholder who holds the role.  For example if you have a 'supervisor' role you would find\
> the URL to delete by looking up the supervised cardholder, not the supervisor.\
> \
> In the interest of forward compability, \[do not build the URL\
> yourself]\(forward-compatibility-hateoas).<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/roles/{secondary_id}":{"delete":{"tags":["Cardholders"],"summary":"Remove a relationship","description":"This call severs a relationship between two cardholders.\n\nYou can find the URL in the `relationships` block in the [cardholder\nobject](models#cardholderdetail) of the cardholder who has the relationship, not the\ncardholder who holds the role.  For example if you have a 'supervisor' role you would find\nthe URL to delete by looking up the supervised cardholder, not the supervisor.\n\nIn the interest of forward compability, [do not build the URL\nyourself](forward-compatibility-hateoas).\n","responses":{"204":{"description":"Success."},"403":{"description":"The operator does not have a privilege that allows editing that cardholder."},"404":{"description":"That is not the URL of a relationship.  Perhaps it is deleted already."}}}}}}
```

## Change a cardholder's location

> This call updates a cardholder's location (moves them) to a target access zone. Added in 8.20.\
> \
> \[Do not code this URL]\(forward-compatibility-hateoas) into your application.  Take\
> it from the \`updateLocation.href\` field in a \[cardholder\
> response]\(models#cardholderdetail).\
> \
> The POST expects a document which contains an href to the target access zone.  The\
> recommended way of getting access zone hrefs is through an \[access zones\
> call]\(access-zones#get-api-access\_zones-update\_cardholder\_location) added in 8.20\
> that returns you the access zones to which you are allowed to move cardholders, according to\
> your operator privileges.\
> \
> You can also get access zone hrefs from the \[items\
> controller]\(items#get-api-items).\
> \
> It is only possible to put a cardholder into "nowhere", also known as "outside the system",\
> in v9.60 or later.  In earlier versions the only place you can move them is a new access\
> zone.\
> \
> Unlike cardholder PATCHes, this operation is not blocked by another operator holding a write\
> lock on the cardholder.\
> \
> Note that unless you are adding a historical movement event (see below) your REST operator\
> will need the "Manage Cardholder Location" privilege in the division of the target access\
> zone and "View Cardholder" on the cardholder itself.\
> \
> \### Adding historical movements\
> \
> It is possible to add a movement event dated in the past, making it look as though the\
> cardholder moved at some point in history.  For this to work:\
> \
> \* your server must have the Regulated Zones customisation licence, and\
> \
> \* the time must be in the past, more recent than the cardholder's most recent movement, and\
> &#x20; more recent than the "Maximum age of historical manual access events" setting in the\
> &#x20; "Operator defaults" tab of the server properties, and\
> \
> \* your operator must have the 'Manage historical cardholder location' privilege in the\
> &#x20; division of both the cardholder and the target access zone.\
> \
> That privilege is not included in 'Advanced user', by the way.  Not that your API operators\
> would ever have 'Advanced'.\
> \
> Added in 9.40.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/update_location":{"post":{"tags":["Cardholders"],"summary":"Change a cardholder's location","description":"This call updates a cardholder's location (moves them) to a target access zone. Added in 8.20.\n\n[Do not code this URL](forward-compatibility-hateoas) into your application.  Take\nit from the `updateLocation.href` field in a [cardholder\nresponse](models#cardholderdetail).\n\nThe POST expects a document which contains an href to the target access zone.  The\nrecommended way of getting access zone hrefs is through an [access zones\ncall](access-zones#get-api-access_zones-update_cardholder_location) added in 8.20\nthat returns you the access zones to which you are allowed to move cardholders, according to\nyour operator privileges.\n\nYou can also get access zone hrefs from the [items\ncontroller](items#get-api-items).\n\nIt is only possible to put a cardholder into \"nowhere\", also known as \"outside the system\",\nin v9.60 or later.  In earlier versions the only place you can move them is a new access\nzone.\n\nUnlike cardholder PATCHes, this operation is not blocked by another operator holding a write\nlock on the cardholder.\n\nNote that unless you are adding a historical movement event (see below) your REST operator\nwill need the \"Manage Cardholder Location\" privilege in the division of the target access\nzone and \"View Cardholder\" on the cardholder itself.\n\n### Adding historical movements\n\nIt is possible to add a movement event dated in the past, making it look as though the\ncardholder moved at some point in history.  For this to work:\n\n* your server must have the Regulated Zones customisation licence, and\n\n* the time must be in the past, more recent than the cardholder's most recent movement, and\n  more recent than the \"Maximum age of historical manual access events\" setting in the\n  \"Operator defaults\" tab of the server properties, and\n\n* your operator must have the 'Manage historical cardholder location' privilege in the\n  division of both the cardholder and the target access zone.\n\nThat privilege is not included in 'Advanced user', by the way.  Not that your API operators\nwould ever have 'Advanced'.\n\nAdded in 9.40.\n","requestBody":{"description":"The body of the request must contain the href of the target access zone.\n","content":{"application/json":{"schema":{"type":"object","properties":{"accessZone":{"type":"object","properties":{"href":{"type":"string","format":"uri-reference","description":"The href of the access zone into which you want to move your cardholder.\nUse an href from the [access zones\ncontroller](access-zones#get-api-access_zones-update_cardholder_location),\nor the [items controller](items) if you do not have a RESTStatus or (in\n8.60) RESTOverrides licence.\n\nIn 9.60 and later, use the empty string `\"\"` to move a cardholder out of all\nzones (known as \"outside the system\").\n"}}},"time":{"type":"string","format":"date-time","description":"First available in 9.40, this changes the request from a real-time move into an\nhistorical one.  It needs a different licence and privilege, covered in this\nroute's description.  Also, this field's value must be in the past, more recent\nthan the cardholder's most recent movement, and more recent than the \"Maximum\nage of historical manual access events\" server property.\n"}}}}},"required":true},"responses":{"200":{"description":"Success.  Future versions will return feedback from the server."},"204":{"description":"Success."},"400":{"description":"The cardholder ID or access zone href is invalid.\n"},"403":{"description":"The operator does not have a privilege ('Manage Cardholder Location') that allows moving\nthis cardholder to the target zone.\n"}}}}}}
```

## Find out which fields you can edit

> This tells you which fields your REST operator can edit based on its privileges.\
> \
> Gallagher uses this call for rapidly-evolving internal applications.  As such, its results\
> are tuned to those applications and are subject to change.  Rather than relying on this\
> call, we suggest that you simply give your REST operator the privileges it needs.\
> \
> Note that this method returns 400 if there are two\
> PDFs in the system with the same name.  CC insists on unique names for items when you create\
> them but you can end up with duplicates when you form a multiserver cluster out of\
> standalone installations.  You should move on that, because having two PDFs with the same\
> name will bewilder your operational staff.<br>

```json
{"openapi":"3.2.0","info":{"title":"Command Centre REST API","version":"2.0"},"tags":[{"name":"Cardholders","description":"A cardholder is a user account.\n\nThese methods give you read and write access to the cardholder data in the Command\nCentre database.  You can download cardholders, create them, search them by name or\nPersonal Data Field (PDF) value, and work on them as you would in the interactive\nclients.\n\nThe first use case below introduces the main entry point.  It is a paginated search interface\nthat gives you basic data for any number of cardholders.  You will not receive many fields by\ndefault, but you can ask for more using the `fields` query parameter.\n\nA field called `href` serves both as a unique identifier for the cardholder and the location of\nwhat we call his or her _details_ page.  You can submit an HTTP GET to that href to retrieve the\nentire cardholder record, or an HTTP PATCH to update it, or an HTTP DELETE to remove it.  Be\ncareful:  there is no coming back from deleting a cardholder.\n\n## Efficiency tips\n\n* When searching for a cardholder by personal data or any object by name, if you have the\n  exact string, put it in quotes `\"...\"`.  It is still a case-insensitive search but it attempts\n  to match the entire string.  Without the quotes Command Centre will perform a substring\n  search, which is slower and may return more results than you need.  In one test, a substring\n  search of half a million cardholders took more than ten seconds, but with quotes it took a\n  tenth of a second.  That is a 100x speedup.  If the server believes you have omitted the\n  quotes in error, it will include a polite reminder in its results.\n\n* When downloading a significant number of items, sort by ID.\n\n* Set your page size as large as your client can handle.  The default was 100 before 8.70; using\n  the `top` parameter to take it to 1000 or more gives _much_ better throughput.\n\nTo give you an idea of the effect of setting `sort` and `top`, here are some timings for a\ndownload of 200,000 cardholders from CC 8.40:\n\n| Sorting           | Pagination    | Time\n| ----------------- | ------------- | --------\n| Default (by name) | Default (100) | 28 minutes\n| `sort=id`         | Default (100) | 5 minutes\n| Default (by name) | `top=1000`    | 3.5 minutes\n| `sort=id`         | `top=1000`    | 1 minute\n\n* In version 8.00 or later, use the [`fields` parameter](cardholders#field-names-in-query-parameters) to add fields\n  to the results of a search, saving you GETting the details page.\n\n  For example, to see the names, personal data fields, and access groups of all your cardholders:\n\n    `GET /api/cardholders?sort=id&top=10000 &fields=firstname,lastname,personalDataFields,accessGroups`\n\n  It adds to the payload size, though, so only do this if you are certain that the search will\n  not return too many cardholders you are not interested in.\n\n* If you are writing an integration that synchronises users from a source of truth to Command\n  Centre, see the 'Synchronising a user directory' use case.\n\n* If you must get an object's details page, and you are on v8.00 or later, using the [`fields`\n  parameter](cardholders#field-names-in-query-parameters) will still save work.  Be explicit with what you want:  do\n  not use `fields=defaults` unless you need every field that 'defaults' gives you and you\n  accept that we may add fields to the defaults in future.\n\n* When creating cardholders, assign their cards, groups, and other attributes in the POST.  This is\n  significantly quicker than sending a handful of PATCHes chasing after your POST, and if you\n  are interrupted, you will not end up with a partially-configured cardholder.\n\n* When modifying a cardholder, do all your operations in one PATCH.  This is much quicker than\n  doing it in several smaller PATCHes.\n\n## Write-locked cardholders\n\nIf an operator has unsaved changes on a cardholder object when your integration\nattempts to update it, your PATCH will fail with a 409 error.  The message will tell\nyou the operator and workstation that is holding the lock; we suggest your application\nlogs that and tries its update again later.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence.  Licences\nthat enable additional features are described in each route.\n\n## Cardholder use cases\n\n### Searching for cardholders by name\n1. `GET /api`.\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), appending a search term to narrow the results and\n   the `fields` parameter to add the fields you need.\n\n3. Process the results, following the `next` link until there isn't one.\n\n### Downloading all cardholders \n1. `GET /api`\n\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders).  This is effectively a cardholder search with no\n   filters.  You should add `sort` and `top` query parameters to sort by ID and increase the\n   number of cardholders per page:  see the efficiency tips!\n\n3. Process the bundle of cardholders in the result.\n4. Follow the link at `next.href` if there is one, and repeat.\n\n### Synchronising a user directory\n\nThis is a common use case, described in the section on [Cardholder\nchanges](cardholder-change-tracking).  It shows how to:\n\n1. Get a bookmark at the head of the queue of cardholder changes.\n2. Do a one-off sync of all cardholders.\n3. Loop to stay up to date, starting with the bookmark.\n\nThat is how Gallagher's cardholder-synchronising integrations work.\n\n### Searching for cardholders by Personal Data Field value\n\nSay you want to find a cardholder with a particular employee ID, and Command Centre is\nholding that in a personal data field called \"employee_ID\".\n\nFirst, reconsider, and look at the 'Synchronising a user directory' use case above.  If you are\ngoing to be doing this for many cardholders, GETting cardholders in bulk and filtering out the\ninteresting ones client-side is more efficient than a large number of unique PDF searches.  You\ndon't have to get all cardholders; you could get all cardholders in one division, or all\ncardholders with a non-blank value for your PDF (shown below), or a combination.  It will be\nquicker than a sequence of searches that return one cardholder each.\n\nBefore you can start a cardholder search, you must find the PDF's identifier.\n\n1. `GET /api`.\n\n2. v8.10 or later:  follow the link at `features.personalDataFields.personalDataFields.href`\n   [↪](pdf-definitions#get-api-personal_data_fields), appending the query\n   `name=\"employee_id\"`.  This will return every PDF in the system with that name.  There will\n   be more than one, in rare cases.\n\n2. Earlier versions:  follow the link at `features.items.items.href`\n   [↪](items#get-api-items), appending the query `name=\"employee_id\"` (after a\n   `?` or `&`, of course).  This will return every item in the system with that name.  Pick the\n   one with a type name of 'Personal Data Field'.  Or you could append `type=33` to the item\n   search:  you will only be shown PDFs.\n\n3. Note the ID of the item.  It will be a short alphanumeric.\n\nCase does not matter when searching by name but remember the quotes:  Command Centre will\nperform a substring match if you omit them, which is vastly slower and (for the example above)\nreturn you the PDFs 'previous_employee_ID' and 'employee_ID_allocated_flag', if you have\nPDFs with those names.\n\nNow you can use that PDF ID in a cardholder search.\n\n1. Recall the JSON object you received from `GET /api`.\n2. Follow the link at `features.cardholders.cardholders.href`\n   [↪](cardholders#get-api-cardholders), adding a query separator and\n   `pdf_<your_pdf_id>=<your_pdf_value>`, without the angle brackets.\n\nThe results will only include cardholders who have *\\<your_pdf_value>* as a substring of\nthe Personal Data Field with ID *\\<your_pdf_id>*.  Again, the search is\ncase-insensitive and you should bookend *\\<your_pdf_value>* with quotes if you want to anchor it at\neach end.  In our example we would `GET /api/cardholders?pdf_345=\"EID8888\"`.\n\nUse a percent sign `%` for *\\<your_pdf_value>* if you want to see all cardholders who have a\nnon-blank value for that PDF.\n\nThe server will reply with a default set of fields for your cardholder.  If they are not what\nyou'd like, you can use the `fields` query parameter to change them.\n\nSee the [cardholder GET](cardholders#get-api-cardholders) for more.\n\n### Creating a cardholder\n1. `GET /api`.\n2. Use the link at `features.accessGroups.accessGroups.href` to find the hrefs of access groups\nyou wish to add your new cardholder to.\n\n3. Do the same for the competencies, relationships (`roles`), lockers, PDF definitions, and\ncards/credentials (`cardTypes`) that your new cardholder needs, using other URLs in the\n`features` section of `/api`.  In v8.10, the card types that your operator has the privilege to\nassign are at a new URL given in the field `features.cardTypes.assign.href`\n[↪](cardholders#get-api-card_types-assign).\n\n4. Compose a JSON body using [this example](models#cardholderpost) and [this\ndetail](models#cardholderdetail), then send it in a POST to the href at\n`features.cardholders.cardholders.href` [↪](cardholders#post-api-cardholders) on the\n`/api` page.\n\n### Modifying a cardholder\n1. Find your cardholder using one of the processes above.\n2. If you need the current values before you update them, use the `fields` parameter to add\nfields (such as accessGroups, cards, or competencies) to the search results.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder's href with a\ndocument describing the additions, deletions, and modifications you wish to make to\nthe cardholder and his or her associations.\n\nIn its simplest form, your document could be a collection of key/value pairs much the same as\nyou receive from a GET to the same href.  [More complex forms](models#cardholderpatch)\nallow adding, modifying, and removing cards, competencies, group membership, PDFs, and\nrelationships.  Three examples follow.\n\n### Removing a cardholder from groups\n\n1. Find your cardholder using one of the processes above.\n2. Follow the link to their details page or add `fields=accessGroups` to the search.  That will\n   show all their access group memberships.\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with the hrefs of the\n   memberships you want to delete in an array called `remove` inside a block called\n   `accessGroups`.\n\nThe [access groups](access-groups) section describes what to do when you want to\nstart with a group and remove several cardholders from it.\n\n### Removing all group memberships, competencies, relationships, cards, or lockers\n\nThis shows how you could remove a cardholder from all his or her access groups, but you could\njust as simply apply the process to other fields that come back as arrays, such as competencies,\nrelationships, operator groups, cards, and lockers.\n\n1. GET your cardholder's details page, or search with `accessGroups` in the field list.  Either\n   way, you will receive all their access group memberships.\n\n2. Take the array at `accessGroups`, rename it to `remove`, and put it in an object called\n   `accessGroups` at the root level of a [JSON object](models#cardholderpatch).\n\n3. [PATCH](cardholders#patch-api-cardholders-id-) the cardholder with that JSON object.\n   That will remove the cardholder from all the groups that came back in the GET.\n\n### Assigning a new card to a cardholder\n\n1. `GET /api`.\n\n2. Follow the link at\n`features.cardTypes.cardTypes.href`[↪](cardholders#get-api-card_types) to find the href\nfor the new card's type.\n\n3. If you want to create a card in a state other than the default, get it from the same page.\n\n4. Find the href for the cardholder using one of the search methods above.\n5. Optional:  follow the link at `edit.href` on the cardholder page.  If there is a link at\n   `update.href` of the results, your REST client has permission to modify the cardholder.\n4. PATCH the cardholder's href according to the [cardholder patch\n   schema](models#cardholderpatch).\n\n### Deleting a cardholder's PDF value\n1. Find the URL of your cardholder using one of the processes above.\n4. [PATCH](cardholders#patch-api-cardholders-id-) your cardholder with this in the body:\n  `{ \"@name_of_PDF\": null }`\n\n\n### Deleting a cardholder\n1. Find your cardholder using one of the processes above.\n2. Send an HTTP DELETE to the cardholder's href.\n\n### Updating a cardholder's location\n1. Find the href of your target access zone using the link at\n   `features.cardholders.updateLocationAccessZones`\n   [↪](access-zones#get-api-access_zones-update_cardholder_location) in the results of\n   `GET /api`.\n\n1. Find your cardholder using one of the processes above.  If you are using\n   [search](cardholders#get-api-cardholders), add `updateLocation` to the `fields`\n   parameter to save you having to GET their details page later.\n\n2. Send an [HTTP POST](cardholders#post-api-cardholders-id-update_location) to that\n   cardholder's `updateLocation.href` link.\n\n### Finding which access zones and doors a cardholder can access\n1. GET your cardholder's details page, or search cardholders with `accessGroups` in the field\nlist.  Either way, you will receive all their access group memberships.\n2. Iterate through all the access groups looking into their `access` blocks for access zones and\nschedules.  Recurse up the access group hierarchy by following each group's `parent.href`.\n\nNow you have the cardholder's access zones.  If you want doors:\n\n3. For each access zone, GET its href and look in its `doors` block.\n\n### Finding recently-created cardholders\n\nInforming you when Command Centre creates cardholders is a function of the [Cardholder\nchanges](cardholder-change-tracking) call, described there.\n\nHowever for that to work you need to make API calls before and after the cardholders are\ncreated.  If they already exist, a workaround is to get a page of cardholders sorted by\ndatabase ID _descending_ using `sort=-id&top=10`.  Change the ten to the number of cardholders\nyou'd like, obviously.  Because Command Centre allocates database IDs to new items in\nincreasing order, the first page you get will be the most-recently created cardholders.  This\nis not suitable for use in a production system!  Use the cardholder changes call instead.\n\n## Field names in query parameters\n\nThe cardholder [search](cardholders#get-api-cardholders) and\n[details](cardholders#get-api-cardholders-id-) operations' `fields` parameter and the [change\ntracking](cardholders#get-api-cardholders-changes) operation's `fields` and `filter` parameters\ntake a list of field names.  Other sections of this document describe how those parameters\naffect the API calls, but their format is the same so this section will cover it once.\n\nYou can form the name of a field by joining the components of its JSON path with dots.  Fields\nat the root level of the cardholder object, such as `firstName` and `@emailAddress` (a PDF),\nhave just one component.  Fields that are one level down, such as `accessGroups.status`, have\ntwo.\n\nTreat the string matches as case sensitive:  use `lastName` rather than `lastname`.\n\nThe string must not contain any spaces.  Just alphanumerics, underscores, commas, and dots.\n\nTo serve as examples, this is the list you can choose from in 8.30:\n\n`href`, `id`, `firstName`, `lastName`, `shortName`, `description`, `authorised`,\n`lastSuccessfulAccessTime`, `lastSuccessfulAccessZone`, `division`,\n`personalDataFields`, `cards`, `accessGroups`, `competencies`, `notes`, `notifications`,\n`relationships`, `lockers`,\n`cards.href`, `cards.number`, `cards.from`, `cards.until`, `cards.cardSerialNumber`, `cards.type`,\n`cards.status`, `cards.invitation`, `cards.issueLevel`, `cards.pivData`,\n`cards.pivData.chuid`, `cards.pivData.pivStatus`, `cards.pivData.lastCheckTime`,\n`cards.pivData.chuid.hash`, `cards.pivData.chuid.fascn`, `cards.pivData.chuid.duns`,\n`cards.pivData.chuid.orgIdentifier`,\n`cards.invitation.href`, `cards.invitation.singleFactorOnly`, `cards.invitation.email`,\n`cards.invitation.mobile`,\n`notifications.enabled`, `notifications.from`, `notifications.until`,\n`accessGroups.href`, `accessGroups.accessGroup`, `accessGroups.from`,\n`accessGroups.until`, `accessGroups.status`,\n`relationships.href`, `relationships.cardholder`, `relationships.role`,\n`lockers.href`, `lockers.locker`, `lockers.from`, `lockers.until`\n\nLater versions of Command Centre added many more.  See the\n[details](cardholders#get-api-cardholders-id-) operation for the complete list of\ncardholder fields.\n\nA special value `default` applies a default set of fields that varies with the call you're\nmaking.\n\n`personalDataFields` is also special.  It will give you the personalDataDefinitions block plus\nall the PDF values at the root level with their names preceded by '@'-signs.\n\n## Licensing\n\nAll of the API calls described here are available with the RESTCardholders licence, with the\nfollowing exceptions and notes:\n\n  * Visitor management calls need both RESTCardholders and VisitorManagement.\n\n  * Creating, modifying, and deleting cardholders' connections to competencies requires the\n    RESTCardholders licence.\n\n  * Managing access group memberships is enabled by the RESTCardholders licence.  Managing\n    access group items, card types, personal data definitions, and roles is coming soon.\n\n  * Lockers and locker banks are also available with the RESTStatus licence, and a subset of\n    their fields (enough to allow overriding them) are available with the RESTOverrides licence.\n\nThe server will return a 403 if you attempt an operation for which the server is not licensed.\n"}],"servers":[{"url":"https://127.0.0.1:8904"}],"security":[{"API_key":[]},{"basic":[]}],"components":{"securitySchemes":{"API_key":{"type":"apiKey","name":"Authorization","in":"header","description":"\nClients authenticate by including a pre-shared API key in the `Authorization` header of each\nrequest after an authorisation method of `GGL-API-KEY`:\n\n`Authorization: GGL-API-KEY C774-B01F-D695-AA4B-215F-A158-AC22-ADEB`\n\nFor a fuller description see [API authentication](api-authentication).\n"},"basic":{"type":"http","scheme":"basic","description":"This is HTTP Basic authentication using a blank username and the API key as the password.\n\nExample:  `Authorization: Basic OkM3NzQtQjAxRi1ENjk1LUFBNEItMjE1Ri1BMTU4LUFDMjItQURFQg==`\n\nThat example is equivalent to the example in the description of the api_Key method:\nprepending a colon to the API key beginning with `C774` then encoding it into Base64\nproduces the 56 characters beginning with `OkM3`.\n\nThe colon is a separator between a username, which is blank in our case, and the password,\nwhich is our API key.\n\nFor a fuller description see [API authentication](api-authentication)\n"}}},"paths":{"/api/cardholders/{id}/edit":{"get":{"tags":["Cardholders"],"summary":"Find out which fields you can edit","description":"This tells you which fields your REST operator can edit based on its privileges.\n\nGallagher uses this call for rapidly-evolving internal applications.  As such, its results\nare tuned to those applications and are subject to change.  Rather than relying on this\ncall, we suggest that you simply give your REST operator the privileges it needs.\n\nNote that this method returns 400 if there are two\nPDFs in the system with the same name.  CC insists on unique names for items when you create\nthem but you can end up with duplicates when you form a multiserver cluster out of\nstandalone installations.  You should move on that, because having two PDFs with the same\nname will bewilder your operational staff.\n","responses":{"200":{"description":"Success."},"403":{"description":"You do not have privileges for the operation."}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.security.gallagher.com/knowledge-hub/developer/rest-api/cardholders.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
