# Tags

Provision the digital identities that get scanned.


A **tag** is the dynamic NFC identity bound to a physical item. You provision tags
under a team; later, each tag is written onto a physical chip and, once in the
field, scanned to produce [verifications](/docs/api/verifications).

## Two ways to get physical tags

- **Preconfigured by TAGBASE.** The default, and what most teams use. Order
  tags and we ship them written and ready to scan: apply them and they work.
  We make sure the chips always match the protocol you provision.
- **Written by you.** For tests and small batches. Provision tags through
  this API, then write them onto blank chips with
  [the TAGBASE Writer app](/docs/guides/writer-app). You source compatible
  blank chips yourself; the platform only supports the chips listed under
  [`protocol`](#the-protocol-attribute).

## Fields

| Field      | Type   | Notes                                                       |
|------------|--------|-------------------------------------------------------------|
| `id`       | string | `tag_`-prefixed, assigned by the platform and returned at creation. |
| `protocol` | string | Required at creation. The tag protocol to provision (see below). Returned with every tag. |
| `url`      | string | Required at creation. The address written to the chip and opened when the tag is scanned. You choose it. |
| `comment`  | string | Optional, max 50 characters. A human-readable label. Makes it easy to see what any tag is at a glance, e.g. a product name. Writer apps can display it to identify which physical item a tag belongs to. |
| `session_duration` | integer | Optional, defaults to `600`. How long a [session](/docs/api/sessions) on this tag stays open between its first and second scan, in seconds (`1` to `3600`). |
| `status`   | string | Lifecycle state (see below). Returned when you retrieve a tag. |
| `configured_at` | string | ISO 8601 timestamp when the tag was written to a chip, or `null`. Returned when you retrieve a tag. |
| `inserted_at` | string | ISO 8601 timestamp when the tag was provisioned. |
| `updated_at` | string | ISO 8601 timestamp of the last change to the tag, including the write that configured it. |

A tag also carries a lifecycle `status` that advances as the tag is manufactured:

| Status       | Meaning                                                       |
|--------------|---------------------------------------------------------------|
| `created`    | Provisioned in the platform; not yet written to a chip.       |
| `configured` | Written to a chip and ready to be scanned in the field.       |

A tag can only be verified once it reaches `configured`. Before that it has no
chip behind it, so a scan against it returns `404` (see
[Verifications](/docs/api/verifications)).

### The `protocol` attribute

`protocol` selects which tag protocol the chip uses. The currently supported
values are:

| Value                     | Chip                  |
|---------------------------|-----------------------|
| `ntag_424_dna`            | NTAG 424 DNA          |
| `ntag_424_dna_tag_tamper` | NTAG 424 DNA TagTamper |
| `ntag_223_dna`            | NTAG 223 DNA          |

Use the identifier (left column) as the `protocol` value. Any other value is
rejected with `422`.

### The `url` attribute

`url` is the address written onto the chip and opened when the tag is scanned:
your verification landing page. **You choose it freely**: it can live on
your own custom domain, in whatever shape you like. The platform writes exactly
what you provide and enforces no format.

The platform assigns the `id`, so the URL can't contain it. Keep your own
mapping from each `url` you send to the `id` returned for it, or read the pairs
back later with [List tags](#list-tags).

## Create tags

```
POST /api/v1/tags
```

Provision a batch of tags under the team whose key you present. Send a
`protocol` and `url` for each; the platform assigns an `id` and returns it. Tags
are stored in `created` status, and each one fires a
[`tag.created` webhook](/docs/api/webhooks).

### Request

A JSON:API **array** under `data`, **1 to 500** resources per request. Each entry:

| Member                 | Type   | Required | Notes                                  |
|------------------------|--------|----------|----------------------------------------|
| `attributes.protocol`  | string | yes      | The tag protocol identifier.           |
| `attributes.url`       | string | yes      | The address to write to the chip. You choose it. |
| `attributes.comment`   | string | no       | A human-readable label, max 50 characters. |
| `attributes.session_duration` | integer | no | Session window in seconds, `1` to `3600`. Defaults to `600` (10 minutes). |

```json
{
  "data": [
    {
      "type": "tags",
      "attributes": {
        "protocol": "ntag_424_dna",
        "url": "https://zanna.example/verify/lonafen/8a3f9c2b"
      }
    }
  ]
}
```

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags \
  -X POST \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{ "data": [ { "type": "tags", "attributes": { "protocol": "ntag_424_dna", "url": "https://zanna.example/verify/lonafen/8a3f9c2b" } } ] }'
```
```js
const res = await fetch("https://platform.tagbase.io/api/v1/tags", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
    "Content-Type": "application/vnd.api+json",
  },
  body: JSON.stringify({
    data: [
      {
        type: "tags",
        attributes: {
          protocol: "ntag_424_dna",
          url: "https://zanna.example/verify/lonafen/8a3f9c2b",
        },
      },
    ],
  }),
});
const tags = (await res.json()).data;
```
```php
$response = $client->post("https://platform.tagbase.io/api/v1/tags", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Content-Type"  => "application/vnd.api+json",
    ],
    "json" => [
        "data" => [[
            "type" => "tags",
            "attributes" => [
                "protocol" => "ntag_424_dna",
                "url"      => "https://zanna.example/verify/lonafen/8a3f9c2b",
            ],
        ]],
    ],
]);
$tags = json_decode((string) $response->getBody(), true)["data"];
```
```elixir
tags =
  Req.post!("https://platform.tagbase.io/api/v1/tags",
    headers: [
      {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
      {"content-type", "application/vnd.api+json"}
    ],
    json: %{
      data: [
        %{
          type: "tags",
          attributes: %{
            protocol: "ntag_424_dna",
            url: "https://zanna.example/verify/lonafen/8a3f9c2b"
          }
        }
      ]
    }
  ).body["data"]
```
<!-- /tabs -->

### Response: `201 Created`

A JSON:API **array** of the created tags. Each entry pairs the platform-assigned
`id` with the `url` it was created from, so you can map them back to your records.

```json
{
  "data": [
    {
      "type": "tags",
      "id": "tag_XPg3Zq7gEExNezDWWW7Zzo",
      "attributes": {
        "protocol": "ntag_424_dna",
        "url": "https://zanna.example/verify/lonafen/8a3f9c2b",
        "comment": null,
        "session_duration": 600,
        "status": "created",
        "configured_at": null,
        "inserted_at": "2026-06-07T09:00:00.000000Z",
        "updated_at": "2026-06-07T09:00:00.000000Z"
      }
    }
  ]
}
```

Tags are returned in `created` status. Writing them onto physical chips happens
separately and asynchronously; your integration holds the ids in the meantime and
learns when each tag advances through [webhooks](/docs/api/webhooks):
`tag.configured` when it's written and ready to scan, `tag.configuration_failed`
if a write fails.

### Errors

| Status | When                                                                |
|--------|---------------------------------------------------------------------|
| `400`  | `data` is not an array of 1 to 500 entries, or any entry is missing `protocol` or `url`. |
| `401`  | Missing, invalid, or revoked key.                                   |
| `422`  | Validation failed, e.g. a duplicate `url` or an unrecognized `protocol`. |

## Retrieve a tag

```
GET /api/v1/tags/:id
```

Fetch a tag you provisioned, including its current lifecycle `status`. The key you
present must own the tag, or the platform responds `404`.

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Accept: application/vnd.api+json"
```
```js
const res = await fetch(
  "https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo",
  {
    headers: {
      "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
      "Accept": "application/vnd.api+json",
    },
  },
);
const tag = await res.json();
```
```php
$response = $client->get("https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Accept"        => "application/vnd.api+json",
    ],
]);
$tag = json_decode((string) $response->getBody(), true);
```
```elixir
tag =
  Req.get!("https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo",
    headers: [
      {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
      {"accept", "application/vnd.api+json"}
    ]
  ).body
```
<!-- /tabs -->

### Response: `200 OK`

```json
{
  "data": {
    "type": "tags",
    "id": "tag_XPg3Zq7gEExNezDWWW7Zzo",
    "attributes": {
      "protocol": "ntag_424_dna",
      "url": "https://zanna.example/verify/lonafen/8a3f9c2b",
      "comment": "Lonafen 50mg",
      "session_duration": 600,
      "status": "configured",
      "configured_at": "2026-06-08T12:34:56.123456Z"
    }
  }
}
```

Polling this endpoint is a fallback for tracking a tag's lifecycle;
[webhooks](/docs/api/webhooks) are the push alternative and fire as the tag
advances.

### Errors

| Status | When                              |
|--------|-----------------------------------|
| `401`  | Missing, invalid, or revoked key. |
| `404`  | No such tag under your team.   |

## Update a tag

```
PATCH /api/v1/tags/:id
```

Update a tag's mutable attributes. The `comment` and `session_duration` are
always editable: they live only in the platform, not on the chip. The `url` can
only change while the tag is still `created`; once `configured` it is physically
on the chip and locked, and a `url` change responds `422`.

### Request

A single JSON:API resource under `data`:

```json
{
  "data": {
    "type": "tags",
    "id": "tag_XPg3Zq7gEExNezDWWW7Zzo",
    "attributes": { "comment": "Lonafen 50mg" }
  }
}
```

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo \
  -X PATCH \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{ "data": { "type": "tags", "id": "tag_XPg3Zq7gEExNezDWWW7Zzo", "attributes": { "comment": "Lonafen 50mg" } } }'
```
```js
const res = await fetch(
  "https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo",
  {
    method: "PATCH",
    headers: {
      "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
      "Content-Type": "application/vnd.api+json",
    },
    body: JSON.stringify({
      data: {
        type: "tags",
        id: "tag_XPg3Zq7gEExNezDWWW7Zzo",
        attributes: { comment: "Lonafen 50mg" },
      },
    }),
  },
);
const tag = await res.json();
```
```php
$response = $client->patch("https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Content-Type"  => "application/vnd.api+json",
    ],
    "json" => [
        "data" => [
            "type"       => "tags",
            "id"         => "tag_XPg3Zq7gEExNezDWWW7Zzo",
            "attributes" => ["comment" => "Lonafen 50mg"],
        ],
    ],
]);
$tag = json_decode((string) $response->getBody(), true);
```
```elixir
tag =
  Req.patch!("https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo",
    headers: [
      {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
      {"content-type", "application/vnd.api+json"}
    ],
    json: %{
      data: %{
        type: "tags",
        id: "tag_XPg3Zq7gEExNezDWWW7Zzo",
        attributes: %{comment: "Lonafen 50mg"}
      }
    }
  ).body
```
<!-- /tabs -->

### Response: `200 OK`

The updated tag, in the same shape as [Retrieve a tag](#retrieve-a-tag).

### Errors

| Status | When                                                            |
|--------|------------------------------------------------------------------|
| `400`  | The body is not a single JSON:API resource with `attributes`.   |
| `401`  | Missing, invalid, or revoked key.                               |
| `404`  | No such tag under your team.                                 |
| `422`  | Validation failed, e.g. the `url` of a `configured` tag, a duplicate `url`, a `comment` over 50 characters, or a `session_duration` outside `1` to `3600`. Each error names the field in `source.pointer`, e.g. `/data/attributes/comment`, with the reason in `detail`. |

## Update many tags

```
PATCH /api/v1/tags
```

Update up to 500 tags in one request. The platform applies the batch as a
single transaction: if it rejects one tag, none of them change. Each resource
carries the same attributes as [Update a tag](#update-a-tag).

Use it when one change on your side touches many tags, for example when you
point a product's tags at a new domain, instead of sending one request per
tag.

### Request

A list of JSON:API resources under `data`, each with an `id`:

```json
{
  "data": [
    {
      "type": "tags",
      "id": "tag_XPg3Zq7gEExNezDWWW7Zzo",
      "attributes": { "url": "https://verify.zanna.example/verify/tag_XPg3Zq7gEExNezDWWW7Zzo" }
    },
    {
      "type": "tags",
      "id": "tag_EQ79SmiZqGA7FHG1ftF1Yt",
      "attributes": { "url": "https://verify.zanna.example/verify/tag_EQ79SmiZqGA7FHG1ftF1Yt" }
    }
  ]
}
```

<!-- tabs -->
```bash cURL
curl https://platform.tagbase.io/api/v1/tags \
  -X PATCH \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{ "data": [{ "type": "tags", "id": "tag_XPg3Zq7gEExNezDWWW7Zzo", "attributes": { "comment": "Lonafen 50mg" } }] }'
```
```js
const res = await fetch("https://platform.tagbase.io/api/v1/tags", {
  method: "PATCH",
  headers: {
    "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
    "Content-Type": "application/vnd.api+json",
  },
  body: JSON.stringify({
    data: tags.map((tag) => ({
      type: "tags",
      id: tag.id,
      attributes: { url: tag.url },
    })),
  }),
});
const updated = await res.json();
```
```php
$response = $client->patch("https://platform.tagbase.io/api/v1/tags", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Content-Type"  => "application/vnd.api+json",
    ],
    "json" => [
        "data" => array_map(fn ($tag) => [
            "type"       => "tags",
            "id"         => $tag["id"],
            "attributes" => ["url" => $tag["url"]],
        ], $tags),
    ],
]);
$updated = json_decode((string) $response->getBody(), true);
```
```elixir
updated =
  Req.patch!("https://platform.tagbase.io/api/v1/tags",
    headers: [
      {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
      {"content-type", "application/vnd.api+json"}
    ],
    json: %{
      data:
        Enum.map(tags, fn tag ->
          %{type: "tags", id: tag.id, attributes: %{url: tag.url}}
        end)
    }
  ).body
```
<!-- /tabs -->

### Response: `200 OK`

The updated tags, in request order, each in the same shape as
[Retrieve a tag](#retrieve-a-tag).

### Errors

| Status | When                                                            |
|--------|------------------------------------------------------------------|
| `400`  | `data` is not a list of 1 to 500 resources, or a resource is missing its `id` or `attributes`. |
| `401`  | Missing, invalid, or revoked key.                               |
| `404`  | One of the tags does not exist under your team. Nothing changed. |
| `422`  | Validation failed for one of the tags, as in [Update a tag](#update-a-tag). Nothing changed. |

## Delete a tag

```
DELETE /api/v1/tags/:id
```

Delete a tag that was never written to a chip. Once a tag is configured, every
scan of its chip resolves to this record, so the platform keeps the tag and
responds `409`. The platform sends the `tag.deleted`
[webhook](/docs/api/webhooks) when it deletes a tag.

<!-- tabs -->
```bash cURL
curl -X DELETE https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Accept: application/vnd.api+json"
```
```js
await fetch(
  "https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo",
  {
    method: "DELETE",
    headers: {
      "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
      "Accept": "application/vnd.api+json",
    },
  },
);
```
```php
$client->delete("https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Accept"        => "application/vnd.api+json",
    ],
]);
```
```elixir
Req.delete!("https://platform.tagbase.io/api/v1/tags/tag_XPg3Zq7gEExNezDWWW7Zzo",
  headers: [
    {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
    {"accept", "application/vnd.api+json"}
  ]
)
```
<!-- /tabs -->

### Response: `204 No Content`

The body is empty.

### Errors

| Status | When                                  |
|--------|---------------------------------------|
| `401`  | Missing, invalid, or revoked key.     |
| `404`  | No such tag under your team.          |
| `409`  | The tag is configured and stays.      |

## Delete many tags

```
DELETE /api/v1/tags
```

Delete up to 500 tags in one request. The platform deletes the tags that were
never written to a chip and keeps the configured ones. Ids that don't belong to
your team are ignored. The response lists which tags went and which stayed.

<!-- tabs -->
```bash cURL
curl -X DELETE https://platform.tagbase.io/api/v1/tags \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/vnd.api+json" \
  -d '{"data": [{"type": "tags", "id": "tag_XPg3Zq7gEExNezDWWW7Zzo"}, {"type": "tags", "id": "tag_7Hq2Wm4nLp9Rv3Tx6Yz8Ab"}]}'
```
```js
const res = await fetch("https://platform.tagbase.io/api/v1/tags", {
  method: "DELETE",
  headers: {
    "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
    "Content-Type": "application/vnd.api+json",
    "Accept": "application/vnd.api+json",
  },
  body: JSON.stringify({
    data: [
      { type: "tags", id: "tag_XPg3Zq7gEExNezDWWW7Zzo" },
      { type: "tags", id: "tag_7Hq2Wm4nLp9Rv3Tx6Yz8Ab" },
    ],
  }),
});
const { meta } = await res.json();
```
```php
$response = $client->delete("https://platform.tagbase.io/api/v1/tags", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Content-Type"  => "application/vnd.api+json",
        "Accept"        => "application/vnd.api+json",
    ],
    "json" => ["data" => [
        ["type" => "tags", "id" => "tag_XPg3Zq7gEExNezDWWW7Zzo"],
        ["type" => "tags", "id" => "tag_7Hq2Wm4nLp9Rv3Tx6Yz8Ab"],
    ]],
]);
$meta = json_decode((string) $response->getBody(), true)["meta"];
```
```elixir
%{"meta" => meta} =
  Req.delete!("https://platform.tagbase.io/api/v1/tags",
    headers: [
      {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
      {"accept", "application/vnd.api+json"}
    ],
    json: %{
      data: [
        %{type: "tags", id: "tag_XPg3Zq7gEExNezDWWW7Zzo"},
        %{type: "tags", id: "tag_7Hq2Wm4nLp9Rv3Tx6Yz8Ab"}
      ]
    }
  ).body
```
<!-- /tabs -->

### Response: `200 OK`

```json
{
  "meta": {
    "deleted": ["tag_XPg3Zq7gEExNezDWWW7Zzo"],
    "kept": ["tag_7Hq2Wm4nLp9Rv3Tx6Yz8Ab"]
  }
}
```

The platform sends the `tag.deleted` [webhook](/docs/api/webhooks) for each
deleted tag.

### Errors

| Status | When                                                   |
|--------|--------------------------------------------------------|
| `400`  | An empty list, more than 500 tags, or a malformed id.  |
| `401`  | Missing, invalid, or revoked key.                      |

## List tags

```
GET /api/v1/tags
```

List the tags belonging to the team whose key you present, most recently
updated first. Use it to recover ids you didn't store, or to reconcile a batch
after a create you aren't sure landed.

The default order is `updated_at` descending, so a tag rises to the front when
it is written or edited. Pass `order_by` and `order_directions` to sort by
something else. The tag `id` always breaks ties, which is what keeps a tag off
two pages at once.

### Query parameters

| Parameter   | Type    | Notes                                                    |
|-------------|---------|----------------------------------------------------------|
| `page`      | integer | 1-based page number. Defaults to `1`.                    |
| `page_size` | integer | Tags per page. Defaults to `10`, maximum `100`. Ask for more and you get 100 back. |
| `q`         | string  | Search. Matches a tag `comment`, or an exact tag id. |
| `order_by` | array | Fields to sort by, e.g. `order_by[]=inserted_at`. Defaults to `updated_at`. One of `id`, `protocol`, `inserted_at`, `updated_at`, `configured_at`, `comment`, `url`, `status`. |
| `order_directions` | array | `asc` or `desc` per `order_by` entry, e.g. `order_directions[]=desc`. Defaults to `asc`. |

<!-- tabs -->
```bash cURL
curl "https://platform.tagbase.io/api/v1/tags?page=1&page_size=100" \
  -H "Authorization: Bearer $TAGBASE_API_KEY" \
  -H "Accept: application/vnd.api+json"
```
```js
const res = await fetch(
  "https://platform.tagbase.io/api/v1/tags?page=1&page_size=100",
  {
    headers: {
      "Authorization": `Bearer ${process.env.TAGBASE_API_KEY}`,
      "Accept": "application/vnd.api+json",
    },
  },
);
const { data, meta } = await res.json();
```
```php
$response = $client->get("https://platform.tagbase.io/api/v1/tags", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("TAGBASE_API_KEY"),
        "Accept"        => "application/vnd.api+json",
    ],
    "query" => ["page" => 1, "page_size" => 100],
]);
$page = json_decode((string) $response->getBody(), true);
```
```elixir
page =
  Req.get!("https://platform.tagbase.io/api/v1/tags",
    params: [page: 1, page_size: 100],
    headers: [
      {"authorization", "Bearer #{System.fetch_env!("TAGBASE_API_KEY")}"},
      {"accept", "application/vnd.api+json"}
    ]
  ).body
```
<!-- /tabs -->

### Response: `200 OK`

A JSON:API array of tags, in the same shape as
[Retrieve a tag](#retrieve-a-tag), plus a `meta` object describing the page.

```json
{
  "data": [
    {
      "type": "tags",
      "id": "tag_XPg3Zq7gEExNezDWWW7Zzo",
      "attributes": {
        "protocol": "ntag_424_dna",
        "url": "https://zanna.example/verify/lonafen/8a3f9c2b",
        "comment": "Lonafen 50mg",
        "session_duration": 600,
        "status": "configured",
        "configured_at": "2026-06-08T12:34:56.123456Z",
        "inserted_at": "2026-06-07T09:00:00.000000Z",
        "updated_at": "2026-06-08T12:34:56.123456Z"
      }
    }
  ],
  "meta": {
    "total_count": 1,
    "page": 1,
    "page_size": 100
  }
}
```

Walk the pages until you have `total_count` tags. The order holds across pages,
so a tag never lands on two of them. The list is live, though: tags you create
or edit while paging arrive at the front and push the later pages along.

### Errors

| Status | When                              |
|--------|-----------------------------------|
| `401`  | Missing, invalid, or revoked key. |

## Notes

- The list covers the team whose key you present, and nothing below it. To read
  a subteam's tags, present that subteam's own key.
- Tags belong to the team whose key created them. To keep tenants isolated,
  create each tenant's tags with that tenant's [subteam](/docs/api/teams)
  key.

