# Dimensions

> Tag keys, their classification and promotion, the customer catalogue and dropped-tag diagnostics

Source: https://www.apitraffic.io/docs/api-reference/dimensions

The dimensions API manages what [Settings → Dimensions](https://www.apitraffic.io/docs/tags#settings--dimensions) shows: the tag keys your SDKs send, the customer catalogue that names their values, and why tags were dropped. Every path is scoped to an account, and an account can only reach its own records.

**Permissions.** Any member can list keys, read the catalogue and diagnostics, and set a key's display name, description and hidden flag. Changing a key's `classification` or `role`, promoting and demoting, and changing the catalogue need an owner or admin (or an API token with the `administrator` role). Ingest tokens can't use this API.

**Personal values.** For a key classified `personal`, values and labels are masked (`d***@acme.com`) unless the caller may view personal tags (owners and admins). This applies to catalogue entries (each has `masked: true`) and to the breakdown (`meta.masked: true`). A masked catalogue list returns the first page only: `search` and `after` are refused with a `400`, since they would let hidden values be tested.

**MCP.** The `list_dimensions` and `get_dimension_values` MCP tools need the Custom reports plan feature; this REST API's listing does not. `list_dimensions` also returns the suggestions, as `suggestedToPromote`, unless it is called with `includeSuggestions: false` (for when only the keys are needed: the saved reports and dashboards are then not read).

All examples use `Authorization: Bearer `. `{key}` is the tag key as your SDK sends it.

## Tag keys

`GET https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions`

List every registered key: the customer key first, then by share of requests.

- **include (query, string)**:
  `suggestions` to also return the keys suggested to promote (`suggestions`). Working them out reads the account's saved reports and dashboards, so they are left out unless asked for

### Response

- **records (array)**:
  The keys. Each has `key`, `displayName`, `description`, `classification` (`none`, `pseudonymous`, `personal` or `secret`), `suggestedClassification`, `role` (`customer` or `none`), `hidden`, `promoted`, `promotionStatus` (`pending`, `backfilling`, `live`, `over_budget`, `demoted` or `null`), `promotedAt`, `promotedBy`, `firstSeenAt`, `lastSeenAt`, `catalogValues`, and `stats`: `distinct1d`, `distinct7d`, `requestShare7d` (0 to 1), `requests7d` and `updatedAt` (all `null` until the nightly scan has run).

- **limits (object)**:
  `maxKeys` (keys the registry holds per account), `registered`, `promoted` (keys promoted now) and `maxPromoted` (your plan's allowance of promoted keys; `null` when it is unlimited). `maxPromoted` is sent to the account owner only; anyone else, and every API token, gets `promotedAtLimit` (`true` when no more keys can be promoted) in its place

- **unregistered (object)**:
  Keys past the limit in the last 7 days: `tags7d` (how many tags used them) and up to 10 `sampleKeys`

- **suggestions (array)**:
  Only with `include=suggestions`. Keys suggested to promote, most used first (up to 20; empty when your plan does not include Custom reports): keys that are not promoted or classified `secret`, which the account's saved reports and dashboards would read from pre-aggregated data once promoted. Each has `key`, `reports` (saved reports it would make fast), `dashboards` (dashboards with at least one such tile, with the dashboard's own filter and scope applied) and `requestShare7d`. A report counts only when it groups by the key and everything else in it is held in pre-aggregated data: a key a report only filters on is not counted. Every report and dashboard in the account counts, including ones private to other people, but only the numbers are returned.

`PATCH https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}`

Change a key. Send at least one field.

- **displayName (body, string)**:
  Up to 120 characters; `""` clears it

- **description (body, string)**:
  Up to 1,000 characters; `""` clears it

- **hidden (body, boolean)**:
  Hide the key from pickers and the list

- **classification (body, string)**:
  `none`, `pseudonymous`, `personal` or `secret`. Owners and admins only. A promoted key, or the customer key, cannot be `secret`

- **role (body, string)**:
  `customer` makes this the account's customer key and takes the role from the key that had it; an unclassified key becomes `pseudonymous`. `none` removes the role. Owners and admins only

```bash
curl -X PATCH https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/customerId \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{ "displayName": "Customer", "role": "customer" }'
```

Returns the key.

`POST https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/promote`

Promote a key to pre-aggregated reports. Needs the Custom reports plan feature and a free promoted-key slot: a full allowance answers `409` `ENTITLEMENT_LIMIT_REACHED`. A key classified `secret` can't be promoted. Promoting a promoted key changes nothing, except at a full allowance, where the `409` comes first. Returns the key, with `promotionStatus` `pending`. New traffic is aggregated from about two minutes after promotion; history is backfilled newest day first (`backfilling`, with `backfillProgress` from 0 to 1), then the key is `live`. A key with more distinct values in a day than the plan aggregates is `over_budget` until the next UTC day.

`POST https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/demote`

Stop aggregating a key. Works on any plan. Returns the key, with `promotionStatus` `demoted`. The aggregates kept stay readable, and promoting the key again fills in only the time they do not cover.

`GET https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/breakdown`

A promoted key by value over a window, from its aggregates. Needs the Custom reports plan feature (reads keep working after a downgrade). The window is clamped to what aggregates keep: 13 months on Business and above, otherwise request retention, and request retention for a key classified `personal`; never past the account's retention entitlement. A key classified `secret` is refused with a `400`.

- **from (query, string)**:
  ISO 8601 window start. Default: 24 hours before `to`

- **to (query, string)**:
  ISO 8601 window end. Default: now

- **bucketSid (query, string)**:
  Only this bucket

- **environmentSid (query, string)**:
  Only this environment

- **limit (query, number)**:
  Values returned, by requests. Default 25, at most 500

### Response

- **records (array)**:
  Per value: `value`, `label` and `attributes` (from the catalogue), `requests`, `serverErrors`, `clientErrors`, `errorRate` (server errors / requests), `requestBytes`, `responseBytes`, and `latency` (`avgMs`, `p50Ms`, `p95Ms`, `maxMs`, `approximate: true`; null when no request had a duration). Values past the per-minute limit are one record with `value: "__other__"` and `other: true`

- **coverage (object)**:
  The window's `requests`, `tagged` (carried the key), `other` (counted as `__other__`), `missing` (no key), `attributedShare` and `otherShare`

- **window (object)**:
  `from` and `to` as read, and `clamped` when the window asked for started before what aggregates keep

- **meta (object)**:
  `source: "aggregates"`, `approximate`, `ready` (false until the key's aggregate source exists), `promoted`, `promotionStatus`, `backfillProgress` (0 to 1), `coveredFrom` (where backfilled history starts, once complete) and `masked` (when values are masked)

A failed read answers like other analytics reads: `504 TELEMETRY_TIMEOUT` (narrow the window), `503 TELEMETRY_OVERLOADED` with `Retry-After`, and so on.

## Catalogue

`GET https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/values`

List a key's catalogue entries, by value.

- **search (query, string)**:
  Matches values and labels containing this text (case-insensitive)

- **limit (query, number)**:
  Default 100, at most 500

- **after (query, string)**:
  The `next` value of the previous page

### Response

- **records (array)**:
  Entries: `value`, `label`, `attributes`, `source` (`api` or `csv`), `updatedAt`, and `masked` when masked

- **hasMore (boolean)**:
  Whether there are more entries

- **next (string)**:
  Pass as `after` for the next page

`PUT https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/values`

Add or replace up to 1,000 entries. Safe to repeat. If any entry is invalid, nothing is saved, and the `400` lists each one in `data.errors` (`index` and `error`).

- **values (body, array, required)**:
  Entries: `value` (required; up to 255 characters), `label` (up to 255 characters), `attributes` (up to 20 strings, numbers or booleans, 4 KB in all; names follow the tag key rules)

```bash
curl -X PUT https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/customerId/values \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{ "values": [ { "value": "org_8f2a", "label": "Acme Corp", "attributes": { "plan": "enterprise", "region": "eu" } } ] }'
```

```json
{ "inserted": 1, "updated": 0 }
```

An account holds up to 1,000,000 entries; a write that would pass that is refused.

`POST https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/values/upload`

Add or replace entries from CSV text (up to 5 MB and 50,000 rows). The first row names the columns: `value` (required), `label`, and any others become attributes; a column named twice is refused. Invalid rows (including a row with more cells than the header) are skipped and reported. The valid rows are written all or nothing: if the file would take the catalogue past its limit, nothing is written. A value repeated in the file keeps its last row.

- **csv (body, string, required)**:
  The CSV text

- **dryRun (body, boolean)**:
  Check every row and return `preview` (the first 20 valid rows) without saving

### Response

- **rows (number)**:
  Data rows in the file

- **valid (number)**:
  Rows that are (or would be) saved

- **invalid (number)**:
  Rows skipped

- **errors (array)**:
  Up to 100 skipped rows: `row` (the line number in the file) and `error`

- **columns (object)**:
  `value`, `label` and `attributes`: the columns read

- **inserted (number)**:
  Without `dryRun`: entries added (and `updated`: entries replaced)

`DELETE https://api.apitraffic.io/v1/accounts/{accountSid}/dimensions/{key}/values/{value}`

Remove one value's entry. URL-encode the value. This removes the label and attributes, not the traffic.

## Dropped tags

`GET https://api.apitraffic.io/v1/accounts/{accountSid}/dimensionDiagnostics`

Tags dropped over the last days, by reason.

- **days (query, number)**:
  Default 7, at most 90

### Response

- **total (number)**:
  Tags dropped in the window

- **byReason (array)**:
  `reason`, `count` and up to 10 `sampleKeys`. Reasons: `key_invalid`, `key_too_long`, `reserved_prefix`, `value_not_scalar`, `over_limit` (more than 32 keys on a request), `unregistered` (past the registry limit; stored and searchable) and `sdk` (refused by an SDK before sending; no keys)

- **byDay (array)**:
  `day`, `reason` and `count`, newest day first

## Activity events

Changes are recorded in the account's activity log: `data.dimension_updated`, `data.dimension_classified`, `data.dimension_promoted`, `data.dimension_demoted` and `data.catalog_changed`.
