# Update tag group

`PUT /api/configuration/tag-groups/{all}`

- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Update tag group](https://support.gladstonesoftware.cloud/reference/configuration/update-tag-group)

## Overview

Updates a tag group and everything hanging off it — its tags, their properties, and the translations of both. A tag group is a named set of options (for example "Membership length" with tags "Monthly" and "Annual") that can be attached to membership types and activities so members can filter and choose.

**This is a full replacement of the whole tree, not a partial update.** Every call rewrites the group's scalar fields, its tag list, all tag properties in the group, and all translations. Concretely:

- A tag that exists today but is missing from your `tags` array is **marked deleted**.
- All tag properties in the group are deleted and rebuilt from what you send, so a tag whose `properties` you omit loses its properties.
- Translations are deleted and re-inserted, for the group and for every tag, so an omitted `translations` array removes the translations that were there.
- Omitting `sortOrder` or `allowMultipleTags` sets them to `0` and `false`.

Fetch the group with [Get tag group by ID or key](get-tag-group-by-id-or-key-with-optional-filtering.md), change what you need in that structure, and send the whole thing back.

**`tags` cannot be omitted.** Leaving it out returns `500` rather than a `400`. Send the full array, and give every tag at least `"properties": []`.

There is no create endpoint for tag groups — `POST /api/configuration/tag-groups` is deliberately unsupported. Groups are provisioned for you; this endpoint is how you change them. A successful update returns `204 No Content` and the change is visible on the next read of tag groups and membership types for every site.

## Endpoint

`PUT {base_url}/api/configuration/tag-groups/{all}`

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

## Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `tagGroupId` | string | Yes | The tag group's numeric identifier, as returned in `id` by [List tag groups](get-all-tag-groups-with-optional-type-filtering.md). It must parse as an integer even though the parameter is typed as a string — passing the group's `key` here fails with `400`, not `404`. An id that parses but matches no group fails with `404`. |

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | No | **Ignored.** The existing key is deliberately left unchanged, because tags and groups are referenced by key elsewhere in the platform. Send the current value or omit it — either way the key does not change. Maximum 50 characters if you do send it. |
| `name` | string | Yes | The group's display name, shown as the heading above its tags. Required on every call and maximum 100 characters; it is written unconditionally, so send the current value if you are not changing it. |
| `description` | string | Yes | Longer explanatory text for the group. Required on every call and maximum 255 characters; written unconditionally, so resend the current value if you are not changing it. |
| `type` | string | Yes | The tag group type key, which decides what the group can be attached to. The shipped values are `MEMBERSHIP`, `ACTIVITIES`, `SYSTEM` and `TIMETABLES`; your environment may have more. Required, maximum 50 characters, and matched exactly against the existing type keys — a value that does not match any type fails with `Tag group type {type} cannot be found.` returned as `404`. Changing it re-points the group at a different type, so resend the current value unless that is what you want. |
| `sortOrder` | integer (int32) | No | Where this group sits relative to other groups when several are displayed together; lower sorts first. Range -32768 to 32767. **Omitting it or sending null sets it to `0`**, which moves the group to the front of the list. |
| `allowMultipleTags` | boolean | No | Whether more than one tag from this group can be selected at once — `false` makes the group behave as a single choice. **Omitting it or sending null sets it to `false`**, silently turning a multi-select group into a single-select one. |
| `tags` | array of object | Yes | The group's complete set of tags. Existing tags are matched by `id`: send an id to update that tag, omit the id to create a new one, and leave a tag out entirely to have it marked deleted. Deletion is soft — the tag is kept but flagged as deleted, so its history and existing associations are not destroyed, but it stops being offered. Although it is not marked as required, omitting it returns `500`, so always send the full array. |
| `tags[].id` | integer (int32) | No | The tag's identifier, as returned in `tags[].id` by [Get tag group by ID or key](get-tag-group-by-id-or-key-with-optional-filtering.md). Omit it to create a new tag in this group; send it to update the existing tag with that id. An id that does not belong to this group is treated as a new tag rather than an error. A new tag cannot carry `properties` in the same call — the request fails with `500`; create the tag first, then add its properties in a second call. |
| `tags[].key` | string | Yes | The tag's stable code, used to reference it from membership types and activities and as the key its translations are held against. Required, maximum 50 characters, and restricted to letters, numbers and underscores — anything else fails with `Key can only contain letters, numbers and underscores`. Keys are unique across all tags, not just within this group, so reusing one fails with `409`. |
| `tags[].name` | string | Yes | The tag's display label — what a member sees as the option. Required, maximum 100 characters. |
| `tags[].description` | string | Yes | Longer explanatory text for the tag. Required, maximum 255 characters. |
| `tags[].sortOrder` | integer (int32) | Yes | Where this tag sits among the group's other tags; lower sorts first. Range -32768 to 32767. Genuinely required — sending null or leaving it out fails validation rather than defaulting to `0`. |
| `tags[].isSecondary` | boolean | Yes | Marks the tag as secondary rather than primary within its group. Required — it must be sent explicitly on every tag. |
| `tags[].translations` | array of object | No | Translated text for this tag, held against the tag's `key`. Replaced wholesale on every call: the tag's existing translations are deleted first, so **omitting this array removes them**. Send `[]` only when you intend the tag to have no translations. |
| `tags[].translations[].culture` | string | Yes | The culture code the text is written in, matched against the platform's culture list — the shipped codes include `cy-gb`, `en-gb`, `en-ie`, `en-us`, `de`, `es`, `fr`, `gd` and `pt`. Required, maximum 20 characters. A code that is not in that list fails with `500` rather than a validation error, so check the spelling. |
| `tags[].translations[].type` | string | Yes | Which property of the tag this entry translates — use the property's name, such as `Name` or `Description`. Required, maximum 50 characters. One entry per property per culture. |
| `tags[].translations[].text` | string | Yes | The translated text for the tag's named property in that culture. Required, maximum 255 characters. |
| `tags[].properties` | array of object | No | Extra key-value metadata attached to this tag. Every tag property in the whole group is deleted and rebuilt from the request, so **omitting this array removes the tag's properties**; send `[]` to say "no properties" explicitly. Sending null rather than `[]` returns `500`. Each `tagPropertyTypeKey` may appear at most once per tag. |
| `tags[].properties[].tagPropertyTypeKey` | string | Yes | The key of the property type being set. These are a fixed reference list held by the platform, not free text — the only value shipped as standard is `MEMBERSHIPS_FOR_MULTIPLE_PEOPLE`, and your environment may have more. A key that does not match an existing property type fails with `One or more of property type keys {keys} cannot be found.` returned as `404`. Repeating a key within one tag fails with `400`. |
| `tags[].properties[].value` | string | Yes | The value for that property type, held as free text with no length limit and no type checking — the meaning of the value depends entirely on the property type. Required. |
| `translations` | array of object | No | Translated text for the tag group itself, held against the group's `key`. Replaced wholesale on every call: existing translations are deleted first, so **omitting this array removes them**. |
| `translations[].culture` | string | Yes | The culture code the text is written in, matched against the platform's culture list — the shipped codes include `cy-gb`, `en-gb`, `en-ie`, `en-us`, `de`, `es`, `fr`, `gd` and `pt`. Required, maximum 20 characters. A code that is not in that list fails with `500` rather than a validation error, so check the spelling. |
| `translations[].type` | string | Yes | Which property of the tag group this entry translates — use the property's name, such as `Name` or `Description`. Required, maximum 50 characters. |
| `translations[].text` | string | Yes | The translated text for the group's named property in that culture. Required, maximum 255 characters. |

## Sample request

```bash title="cURL"
curl -X PUT '{base_url}/api/configuration/tag-groups/{all}' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "key": "string",
  "name": "string",
  "description": "string",
  "type": "string",
  "sortOrder": 1,
  "allowMultipleTags": true,
  "tags": [
    {
      "id": 12345,
      "key": "string",
      "name": "string",
      "description": "string",
      "sortOrder": 1,
      "isSecondary": true,
      "translations": [
        {
          "culture": "string",
          "type": "string",
          "text": "string"
        }
      ],
      "properties": [
        {
          "tagPropertyTypeKey": "string",
          "value": "string"
        }
      ]
    }
  ],
  "translations": [
    {
      "culture": "string",
      "type": "string",
      "text": "string"
    }
  ]
}'
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | `tagGroupId` is not an integer — `{tagGroupId} is not a valid Tag Group ID`. Sending the group's key instead of its id lands here. |
| `400` | Bad request | A required field is missing or over length, or `tags[].key` contains a character outside letters, numbers and underscores. The body is a validation error object keyed by field name. |
| `400` | Bad request | A tag lists the same `tagPropertyTypeKey` twice — `Tag {key} has duplicate tagPropertyTypeKeys which is not allowed.` Nothing is written. |
| `404` | Not found | No tag group has that id — `Tag group ID {tagGroupId} cannot be found.` |
| `404` | Not found | `type` does not match an existing tag group type — `Tag group type {type} cannot be found.` |
| `404` | Not found | A `tagPropertyTypeKey` does not match an existing property type — `One or more of property type keys {keys} cannot be found.` The message lists every key you sent, not just the bad one. |
| `409` | Conflict | A `tags[].key` is already used by another tag — `The record updating contains a key already in use`. Tag keys are unique platform-wide, not per group. |
| `500` | Internal server error | `tags` was omitted, or a tag's `properties` was sent as null. Also raised when a new tag (no `id`) carries `properties`, and when a `culture` is not a recognised code. The update is all-or-nothing, so nothing is half-applied. |
