# Tags

A tag is a label you apply to posts and ideas so you can group and filter them. Tags belong to the organization and are shared by everyone in it, rather than to a single channel or person.

The whole tag API is an early preview. It can change, or be withdrawn, without a deprecation period — see [API Standards](https://developers.buffer.com/guides/api-standards.md) for what that means in practice.

## Reading tags

```graphql
query {
  tagsV2(first: 20, input: { organizationId: "your_org_id" }) {
    edges {
      node {
        id
        name
        color
        colorName
        isLocked
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

Tags come back a page at a time, sorted by name in ascending order. Pass the previous page's `pageInfo.endCursor` as `after` to walk the pages — see [Pagination](https://developers.buffer.com/guides/pagination.md). Asking for more than 100 in one call is rejected.

When you already have the ID, `tag(input: { id: ... })` fetches one directly. It resolves to `null` with a `NOT_FOUND` entry in the `errors` array when no tag has that ID.

Reading tags needs the `posts:read` scope.

## Colors

Two fields describe the same color.

`color` is a literal hex triplet such as `#F523F1` — `#` followed by exactly six hex digits. Shorthand such as `#F51` is rejected.

`colorName` is a stable identifier for a Buffer palette color, and is `null` when the tag uses a custom one. Prefer it when you want to render Buffer's own colors rather than reproduce a hex value. It is read-only: there is no way to ask for a palette color by name on write, so send the hex value and read `colorName` back to find out whether it landed on one.

## Locked tags

A tag can be **locked**, which happens when an organization holds more tags than its plan allows, usually after a downgrade. Buffer locks the excess rather than deleting it, so nothing is lost if the plan is upgraded again.

A locked tag can still be read, deleted, and applied to posts. It just cannot be renamed or recolored.

## Creating, editing and deleting tags

Creating, editing and deleting tags need the `posts:write` scope, so a read-only token can list an organization's tags but cannot change them.

```graphql
mutation {
  createTag(input: {
    organizationId: "your_org_id",
    tag: { name: "Summer sales", color: "#F523F1" }
  }) {
    ... on TagActionSuccess {
      tag { id name color colorName isLocked }
    }
    ... on MutationError {
      message
    }
  }
}
```

`updateTag` takes the tag's ID and replaces **both** its name and its color, so send the current value for whichever one you are not changing. It does not take an `organizationId` — the organization comes from the tag. It refuses to edit a locked tag.

`deleteTag` removes the tag from every post, idea and content item carrying it; those items are otherwise untouched, and the deletion cannot be undone. A locked tag can be deleted, and deleting an unlocked one frees a slot, so the organization's oldest locked tag is unlocked in its place.

## What can go wrong

Select `... on MutationError { message }` on every tag mutation. Every error the three mutations can return implements that interface, so the catch-all keeps working as the API changes — see [Error Handling](https://developers.buffer.com/guides/error-handling.md).

Three failures are worth branching on, because the caller can act on each:

| Error | When | What to do |
| --- | --- | --- |
| `DuplicateError` | Another tag in the organization already has that name. Names are compared exactly, so `Summer sales` and `summer sales` can both exist. | Pick a different name. |
| `LimitReachedError` | `createTag` only. The organization is at its plan's tag limit — 3 on the Free plan, 250 on a paid one. | Delete a tag, or upgrade the plan. |
| `InvalidInputError` | The name or the color is malformed — an empty name, a name over 100 characters, a color that is not a six-digit hex triplet. | Correct the input. |

Editing a locked tag is currently refused with `UnauthorizedError`. That reads as a permissions problem, but it is not one — an organization administrator gets the same refusal, because the tag is in a plan-limited state rather than a protected one. Treat it as "this tag cannot be edited right now", not as "this caller lacks access", and expect the error type to change.

`NotFoundError`, `UnauthorizedError`, `UnexpectedError` and `RestProxyError` are also members of the three tag payload unions today, and all four are on their way out — they will be reported in the response's top-level `errors` array instead. Do not write new code that names them. The `... on MutationError { message }` selection above survives their removal; an inline fragment naming one of them does not.

## Tags on posts

A post carries its tags as `tagIds`. See [Content Items](https://developers.buffer.com/guides/content-items.md) for grouping content across channels, where the tags apply to the item as a whole.
