---
id: tags-merge
slug: /apis/tags/merge
title: 'Merge Tags'
description: 'Collapse several tags into one across every asset with POST ~/api/tags/command/merge.'
sidebar_label: 'Merge Tags'
sidebar_position: 5
tags: ['api', 'tags', 'merge', 'admin']
---

Merges several tags into a single tag. Every tag listed in `MergeFrom` is removed from the
assets carrying it, and `MergeTo` is added to those assets in its place.

This is the supported way to **rename a tag** — merge the old name into the new one.

## Request

```text
POST ~/api/tags/command/merge
```

### Payload

```json
{
  "mergeTo": "Regression Tests",
  "mergeFrom": ["RegressionTests", "RegTests"]
}
```

| Field       | Type     | Required | Description                                                                                     |
| ----------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `mergeTo`   | string   | yes      | The surviving tag. May be an existing tag or an entirely new one.                                |
| `mergeFrom` | string[] | yes      | Tags to collapse. Each is removed from every visible asset carrying it.                          |
| `field`     | string   | no       | A [custom tag attribute](/apis/tags/query#custom-tag-fields) to operate on instead of `TaggedWith`. |

:::note Casing on the wire
The published reference showed these fields PascalCased (`MergeTo`, `MergeFrom`). Both work —
the server's JSON binding matches property names case-insensitively — but the API's
JSON serializer is configured for camelCase, so **`mergeTo` / `mergeFrom` is the canonical
form** and is what responses elsewhere in this API use. Existing integrations sending PascalCase
do not need to change.
:::

```bash title="Request"
curl -X POST "https://{host}/{instance}/api/tags/command/merge" \
  -H "Authorization: Bearer <access-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
        "mergeTo": "Regression Tests",
        "mergeFrom": ["RegressionTests", "RegTests"]
      }'
```

:::note Merging into a new tag is allowed
`mergeTo` does not have to exist beforehand. Merging `RegressionTests` and `RegTests` into
`Regression Tests` creates that tag on every affected asset even if nothing was tagged that way
before.
:::

## Behavior

- An asset carrying **more than one** of the `mergeFrom` tags ends up with a single `mergeTo`
  tag, not duplicates.
- An asset that already had `mergeTo` is not double-tagged.
- Tags in `mergeFrom` that do not exist are ignored rather than erroring.
- The operation writes to every affected asset, so it appears in asset history and
  [activity stream](/apis/activity-stream) output attributed to the calling member.

## Permissions and Scope

Merge requires the **`ManageTags`** privilege.

:::warning A merge is only as wide as your visibility
The command acts only on assets the caller can see and edit. If you run it as a project-scoped
member, the `MergeFrom` tags survive on assets outside your scope — and the response will not
tell you. Run vocabulary cleanup as a member with instance-wide visibility, then re-check with
[query all tags](/apis/tags/query-all) to confirm the old names are gone.
:::

:::danger There is no undo
Merge rewrites the `TaggedWith` attribute on every matching asset. There is no batch rollback.
Before merging, capture the current state with
[query all tags](/apis/tags/query-all), or query the affected assets and their tags via
[`query.v1`](/apis/query-v1), so you can reconstruct if the merge turns out to be wrong.
:::

## Related Topics

- [Remove tags](/apis/tags/remove) — purge rather than rename
- [Search tags](/apis/tags/search) — find the variants to merge
- [Tags overview](/apis/tags)
