# Tags and custom fields
Tags and custom fields, which work across every module.
**Common tasks**
- [List tags](#get-organizations-organization-tags)
- [Create a tag](#post-organizations-organization-tags)
**Only in the app:** Nothing. Tags and custom fields can be managed with a token.
**How it works in the app:** [help article](https://infranest.io/docs/tags).
**Scopes:** Read with `monitoring.read`, change with `monitoring.write`. See [Tokens and scopes](../tokens-and-scopes.md#scopes).
[Custom field values](#custom-field-values) 2 · [Custom fields](#custom-fields) 4 · [Tags](#tags) 4
Every endpoint below takes a token in the `Authorization: Bearer` header and answers JSON. Errors use the shared format in [Errors](../errors.md). Base address: `https://api.infranest.io/api`.
## Custom field values
### List custom field values for a record {#get-organizations-organization-custom-field-values}
`GET` `/organizations/{organization}/custom-field-values`
**Scope:** `monitoring.read` · **Changes data:** no
The custom field values set on one record. `entity_type` and `entity_id` are required; returns 404 when the record is not in this organization.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `entity_type`
body · required | string | |
| `entity_id`
body · required | integer | |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`not_found`](../errors.md#not_found), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl "https://api.infranest.io/api/organizations/42/custom-field-values?entity_type=%3Centity_type%3E&entity_id=16" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"
```
**Response** · 200
```json
[
{
"id": 1,
"custom_field_definition_id": 1,
"value": "Platform"
},
{
"id": 2,
"custom_field_definition_id": 2,
"value": "Web platform"
}
]
```
---
### Set a custom field value {#put-organizations-organization-custom-field-values}
`PUT` `/organizations/{organization}/custom-field-values`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Sets the `value` of one field (`definition_id`) on one record (`entity_type`, `entity_id`), checked against the field’s type and options. A null value clears it and returns 204. Requires a plan that includes custom fields.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `entity_type`
body · required | string | |
| `entity_id`
body · required | integer | |
| `definition_id`
body · required | integer | |
| `value`
body | string | |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`validation_failed`](../errors.md#validation_failed), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X PUT "https://api.infranest.io/api/organizations/42/custom-field-values" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"entity_type":"server","entity_id":1,"definition_id":1,"value":"Platform"}'
```
**Response** · 200
```json
{
"id": 1,
"custom_field_definition_id": 1,
"value": "Platform"
}
```
## Custom fields
### List custom fields {#get-organizations-organization-custom-fields}
`GET` `/organizations/{organization}/custom-fields`
**Scope:** `monitoring.read` · **Changes data:** no
The organization’s custom field definitions in display order. Pass `entity_type` to list only the fields that apply to that kind of record.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `entity_type`
body | string | |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`not_found`](../errors.md#not_found), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl "https://api.infranest.io/api/organizations/42/custom-fields" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"
```
**Response** · 200
```json
[
{
"id": 1,
"organization_id": 1,
"entity_types": [
"server",
"domain"
],
"key": "cost_centre",
"label": "Cost centre",
"type": "select",
"options": [
"Platform",
"Marketing"
],
"required": false,
"sort": 0,
"show_on_detail": true,
"show_as_column": true,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"deleted_at": null,
"purge_after": null,
"purge_reminder_sent_at": null,
"deleted_by_id": null,
"deleted_by_label": null,
"deletion_reason": null
},
{
"id": 2,
"organization_id": 1,
"entity_types": [
"server"
],
"key": "owner_team",
"label": "Owning team",
"type": "text",
"options": null,
"required": false,
"sort": 1,
"show_on_detail": true,
"show_as_column": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"deleted_at": null,
"purge_after": null,
"purge_reminder_sent_at": null,
"deleted_by_id": null,
"deleted_by_label": null,
"deletion_reason": null
}
]
```
---
### Create a custom field {#post-organizations-organization-custom-fields}
`POST` `/organizations/{organization}/custom-fields`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Defines a custom field with `entity_types`, a unique `key`, a `label` and a `type`; select and multi-select fields also need `options`. Requires a plan that includes custom fields.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`idempotency_in_progress`](../errors.md#idempotency_in_progress), [`validation_failed`](../errors.md#validation_failed), [`idempotency_key_reused`](../errors.md#idempotency_key_reused), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X POST "https://api.infranest.io/api/organizations/42/custom-fields" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"entity_types":["server"],"key":"cost_center","label":"Cost center","type":"text"}'
```
**Response** · 201 — Created
```json
{
"entity_types": [
"server"
],
"key": "cost_center",
"label": "Cost center",
"type": "text",
"options": null,
"required": false,
"sort": 0,
"show_on_detail": true,
"show_as_column": false,
"organization_id": 1,
"updated_at": "2026-01-15T12:00:00.000000Z",
"created_at": "2026-01-15T12:00:00.000000Z",
"id": 4
}
```
---
### Update a custom field {#patch-organizations-organization-custom-fields-customfielddefinition}
`PATCH` `/organizations/{organization}/custom-fields/{customFieldDefinition}`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Replaces the field’s definition; send the full set of fields, as on create. Requires a plan that includes custom fields.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `customFieldDefinition_id`
path · required | integer | The customFieldDefinition's ID. |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`validation_failed`](../errors.md#validation_failed), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X PATCH "https://api.infranest.io/api/organizations/42/custom-fields/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"entity_types":["server","domain"],"key":"cost_centre","label":"Responsible team","type":"select","options":["Platform","Marketing","Data","Internal tools"]}'
```
**Response** · 200
```json
{
"id": 1,
"organization_id": 1,
"entity_types": [
"server",
"domain"
],
"key": "cost_centre",
"label": "Responsible team",
"type": "select",
"options": [
"Platform",
"Marketing"
],
"required": false,
"sort": 0,
"show_on_detail": true,
"show_as_column": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"deleted_at": null,
"purge_after": null,
"purge_reminder_sent_at": null,
"deleted_by_id": null,
"deleted_by_label": null,
"deletion_reason": null
}
```
---
### Delete a custom field {#delete-organizations-organization-custom-fields-customfielddefinition}
`DELETE` `/organizations/{organization}/custom-fields/{customFieldDefinition}`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Deletes the field definition together with every value stored for it. Requires a plan that includes custom fields.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `customFieldDefinition_id`
path · required | integer | The customFieldDefinition's ID. |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X DELETE "https://api.infranest.io/api/organizations/42/custom-fields/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"
```
**Response** · 204 — no content
## Tags
### List tags {#get-organizations-organization-tags}
`GET` `/organizations/{organization}/tags`
**Scope:** `monitoring.read` · **Changes data:** no
Every tag in the organization, with its colour, whether it names a client, its uptime target and how many resources carry it (`usage_count`).
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`not_found`](../errors.md#not_found), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl "https://api.infranest.io/api/organizations/42/tags" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"
```
**Response** · 200
```json
[
{
"id": 3,
"organization_id": 1,
"name": "Customer-facing",
"color": "#3b82f6",
"uptime_target_pct": null,
"is_client": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"usage_count": 12
},
{
"id": 4,
"organization_id": 1,
"name": "Internal",
"color": "#8b5cf6",
"uptime_target_pct": null,
"is_client": false,
"created_at": "2026-01-15T12:00:00.000000Z",
"usage_count": 11
}
]
```
---
### Create a tag {#post-organizations-organization-tags}
`POST` `/organizations/{organization}/tags`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Creates a tag from a `name` (up to 50 characters) and a hex `color` such as `#ff8800`; `is_client` is optional. Refused with 422 when a tag with that name already exists.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `name`
body · required | string | Must not be greater than 50 characters. |
| `color`
body · required | string | Must match the regex /^#[0-9A-Fa-f]{6}$/. |
| `is_client`
body | boolean | |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`idempotency_in_progress`](../errors.md#idempotency_in_progress), [`validation_failed`](../errors.md#validation_failed), [`idempotency_key_reused`](../errors.md#idempotency_key_reused), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X POST "https://api.infranest.io/api/organizations/42/tags" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name":"Payments","color":"#3B82F6"}'
```
**Response** · 201 — Created
```json
{
"name": "Payments",
"color": "#3B82F6",
"organization_id": 1,
"updated_at": "2026-01-15T12:00:00.000000Z",
"created_at": "2026-01-15T12:00:00.000000Z",
"id": 7
}
```
---
### Update a tag {#patch-organizations-organization-tags-tag}
`PATCH` `/organizations/{organization}/tags/{tag}`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Changes any of `name`, `color`, `is_client` and `uptime_target_pct` (0 to 100, or null to clear it). Refused with 422 when the new name is already taken.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `tag_id`
path · required | integer | The tag's ID. |
| `name`
body | string | Must not be greater than 50 characters. |
| `color`
body | string | Must match the regex /^#[0-9A-Fa-f]{6}$/. |
| `uptime_target_pct`
body | number, nullable | The availability agreed with this client. Null clears it, and a cleared target is reported as "none agreed" rather than as 100%. Must be at least 0. Must not be greater than 100. |
| `is_client`
body | boolean | This tag names a client: attention items on what it tags are grouped under it. |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`validation_failed`](../errors.md#validation_failed), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X PATCH "https://api.infranest.io/api/organizations/42/tags/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"color":"#22C55E","uptime_target_pct":99.9}'
```
**Response** · 200
```json
{
"id": 1,
"organization_id": 1,
"name": "Production",
"color": "#22C55E",
"created_at": "2026-01-15T12:00:00.000000Z",
"updated_at": "2026-01-15T12:00:00.000000Z",
"deleted_at": null,
"purge_after": null,
"purge_reminder_sent_at": null,
"deleted_by_id": null,
"deleted_by_label": null,
"deletion_reason": null,
"uptime_target_pct": "99.900",
"is_client": false
}
```
---
### Delete a tag {#delete-organizations-organization-tags-tag}
`DELETE` `/organizations/{organization}/tags/{tag}`
**Scope:** `monitoring.write` · **Changes data:** yes — only where API writes are switched on for the organization
Deletes the tag and removes it from every resource that carried it. The resources themselves are kept.
| Field | Type | Notes |
|---|---|---|
| `organization_id`
path · required | integer | Your organization's ID. |
| `tag_id`
path · required | integer | The tag's ID. |
**Errors:** [`unauthenticated`](../errors.md#unauthenticated), [`scope_missing`](../errors.md#scope_missing), [`wrong_organization`](../errors.md#wrong_organization), [`test_token_required`](../errors.md#test_token_required), [`test_token_sandbox_only`](../errors.md#test_token_sandbox_only), [`permission_denied`](../errors.md#permission_denied), [`write_disabled`](../errors.md#write_disabled), [`organization_suspended`](../errors.md#organization_suspended), [`not_found`](../errors.md#not_found), [`rate_limited`](../errors.md#rate_limited)
**Request**
```sh
curl -X DELETE "https://api.infranest.io/api/organizations/42/tags/1" \
-H "Authorization: Bearer $INFRANEST_TOKEN" \
-H "Accept: application/json"
```
**Response** · 204 — no content