Skip to content

Custom Fields

Custom Fields let you attach structured metadata to devices that goes beyond the built-in attributes (hostname, OS type, tags, etc.). You define field definitions at the organisation or partner level, then set values on individual devices. This is useful for tracking asset numbers, purchase dates, warranty expiry, department assignments, compliance status, and any other data specific to your environment.


How it works

Custom Fields has two layers:

  1. Field definitions — created via the /api/v1/custom-fields endpoints. A definition declares the field’s name, key, data type, validation options, and which device types it applies to. Definitions are scoped to either an organisation or a partner.
  2. Field values — stored as a JSONB object on each device record (custom_fields column). Values are set by patching the device via PATCH /api/v1/devices/:id with a customFields key-value map. Values are merged with any existing custom fields on the device rather than replacing them.

Field types

Every field definition has a type that controls what values are accepted:

TypeDescriptionExample value
textFree-form string up to 10,000 characters"Rack A, Shelf 3"
numberNumeric value (integer or decimal)42
booleanTrue or falsetrue
dropdownOne of a predefined list of choices"Finance"
dateDate string"2026-06-15"

Validation options

When creating a field definition you can supply an options object to constrain accepted values:

OptionApplies toDescription
choicesdropdownArray of allowed string values. Example: ["Finance", "Engineering", "Sales"]
minnumberMinimum numeric value
maxnumberMaximum numeric value
patterntextRegular expression that text values must match
placeholdertext, number, datePlaceholder hint displayed in the UI

You can also set a defaultValue on the definition. If a device does not have an explicit value for the field, the default is used. Setting required: true indicates that the field should always be populated for applicable devices.


Device type targeting

A definition can optionally include a deviceTypes array to limit which operating systems it applies to. Accepted values are windows, macos, and linux.

When deviceTypes is omitted or set to null, the field applies to all device types.

{
"name": "BitLocker Recovery Key",
"fieldKey": "bitlocker_recovery_key",
"type": "text",
"deviceTypes": ["windows"]
}

Scoping and multi-tenancy

Field definitions are scoped to one of:

ScopeWho can createVisibility
OrganisationOrganisation-scoped users or partner users with access to the organisationVisible only within that organisation
PartnerPartner-scoped usersVisible to the partner and all organisations the partner can access

Access rules

  • Organisation users see definitions scoped to their own organisation plus any definitions scoped to their parent partner.
  • Partner users see definitions scoped to their partner plus definitions scoped to any organisation they can access.
  • System-scoped users see all definitions.

Editing and deleting a definition requires ownership: organisation users can only modify definitions belonging to their organisation, and partner users can only modify definitions belonging to their partner.


Creating a field definition

  1. Choose a human-readable name (1—100 characters) and a machine-readable fieldKey. The key must start with a lowercase letter and contain only lowercase letters, digits, and underscores (regex: ^[a-z][a-z0-9_]*$).

  2. Pick a type from the supported list: text, number, boolean, dropdown, or date.

  3. Optionally set options, required, defaultValue, and deviceTypes.

  4. Send the request:

Terminal window
curl -X POST /api/v1/custom-fields \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Department",
"fieldKey": "department",
"type": "dropdown",
"options": {
"choices": ["Finance", "Engineering", "Sales", "Support"]
},
"required": true
}'

Setting values on devices

Custom field values live in the custom_fields JSONB column on the devices table. To set or update values, send a PATCH request to the device endpoint with a customFields object whose keys correspond to field keys:

Terminal window
curl -X PATCH /api/v1/devices/<device-id> \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"customFields": {
"department": "Engineering",
"asset_number": "AST-2026-0042",
"purchase_date": "2025-11-01"
}
}'

Accepted value types

The device update endpoint validates each custom field value against this schema:

Allowed typeNotes
stringMaximum 10,000 characters
numberInteger or decimal
booleantrue or false
nullRemoves the field from the device

Querying and filtering

Listing field definitions

GET /api/v1/custom-fields returns all field definitions visible to the authenticated user. Query parameters:

ParameterTypeDescription
typestringFilter by field type (text, number, boolean, dropdown, date)
orgIdUUIDFilter to a specific organisation (must be accessible)
deviceTypestringFilter definitions applicable to a device type (windows, macos, linux)
searchstringCase-insensitive search across field name and fieldKey
pagenumberPage number (default: 1)
limitnumberResults per page (default: 50, max: 100)
Terminal window
curl "/api/v1/custom-fields?type=dropdown&search=department" \
-H "Authorization: Bearer <token>"

Filtering devices by custom field values

Custom field values are stored as JSONB on each device. In the current implementation, device list queries (GET /api/v1/devices) do not directly filter by custom field values in query parameters. To find devices with specific custom field values, retrieve the device list and filter client-side, or use PostgreSQL JSONB queries if you have direct database access.


Updating a field definition

Use PATCH /api/v1/custom-fields/:id to update a definition. You can change name, options, required, defaultValue, and deviceTypes. The fieldKey and type are immutable after creation.

Terminal window
curl -X PATCH /api/v1/custom-fields/<field-id> \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"options": {
"choices": ["Finance", "Engineering", "Sales", "Support", "Operations"]
}
}'

Deleting a field definition

DELETE /api/v1/custom-fields/:id removes the definition. This does not automatically remove corresponding values from the custom_fields JSONB on existing devices — those values become orphaned but remain on the device record until explicitly cleared.

Terminal window
curl -X DELETE /api/v1/custom-fields/<field-id> \
-H "Authorization: Bearer <token>"

Audit logging

All mutating operations on custom field definitions are recorded in the audit log:

ActionTrigger
custom_field.createA new field definition is created
custom_field.updateA field definition is modified (logs which fields changed)
custom_field.deleteA field definition is deleted

Device-level custom field value changes are logged under the device.update audit action when the device is patched.


API reference

All endpoints require authentication and an appropriate scope (organization, partner, or system).

Field definitions

MethodPathDescription
GET/api/v1/custom-fieldsList field definitions (paginated, filterable)
GET/api/v1/custom-fields/:idGet a single field definition
POST/api/v1/custom-fieldsCreate a new field definition
PATCH/api/v1/custom-fields/:idUpdate a field definition
DELETE/api/v1/custom-fields/:idDelete a field definition

Device values

MethodPathDescription
PATCH/api/v1/devices/:idSet custom field values (include customFields in request body)
GET/api/v1/devices/:idReturns the device with customFields in the response
GET/api/v1/devicesLists devices; each includes its customFields object

Create field definition request body

{
name: string; // 1-100 characters
fieldKey: string; // 1-100 chars, regex: /^[a-z][a-z0-9_]*$/
type: "text" | "number" | "boolean" | "dropdown" | "date";
options?: {
choices?: string[]; // For dropdown type
min?: number; // For number type
max?: number; // For number type
pattern?: string; // Regex for text type
placeholder?: string;
};
required?: boolean; // Default: false
defaultValue?: unknown;
deviceTypes?: ("windows" | "macos" | "linux")[];
orgId?: string; // UUID - omit for org-scoped users
partnerId?: string; // UUID - omit for partner-scoped users
}

Update field definition request body

{
name?: string; // 1-100 characters
options?: {
choices?: string[];
min?: number;
max?: number;
pattern?: string;
placeholder?: string;
};
required?: boolean;
defaultValue?: unknown;
deviceTypes?: ("windows" | "macos" | "linux")[] | null;
}

Database schema

Field definitions are stored in the custom_field_definitions table:

ColumnTypeDescription
idUUIDPrimary key (auto-generated)
org_idUUIDReferences organizations.id (nullable)
partner_idUUIDReferences partners.id (nullable)
nameVARCHAR(100)Human-readable field name
field_keyVARCHAR(100)Machine-readable key
typeENUMOne of text, number, boolean, dropdown, date
optionsJSONBValidation options (choices, min, max, pattern, placeholder)
requiredBOOLEANWhether the field is required (default: false)
default_valueJSONBDefault value for the field
device_typesTEXT[]Array of applicable OS types
created_atTIMESTAMPCreation timestamp
updated_atTIMESTAMPLast modification timestamp

Device values are stored in the custom_fields JSONB column on the devices table as a flat key-value object.


Troubleshooting

Field key rejected

The fieldKey must match the pattern ^[a-z][a-z0-9_]*$. It must start with a lowercase letter and contain only lowercase letters, digits, and underscores. Uppercase letters, hyphens, spaces, and leading digits are not allowed.

Invalid: Asset-Number, 3rd_floor, Department Name Valid: asset_number, third_floor, department_name

”Provide either orgId or partnerId, not both”

When creating a field definition, supply at most one of orgId or partnerId. A field is scoped to a single tenant level. If you are authenticated as an organisation user, you do not need to supply either — the API infers your organisation from the auth context.

Custom field values not appearing on device

Verify that the PATCH /api/v1/devices/:id request includes a customFields key in the JSON body, not custom_fields. The API expects camelCase property names. Also confirm that each value is a string, number, boolean, or null — objects and arrays are not accepted as values.

Deleted definition but values remain on devices

Deleting a field definition does not cascade to device values. The values in the custom_fields JSONB column on each device persist after the definition is removed. To clean up orphaned values, patch each affected device with the field key set to null.

”Custom field not found” on GET/PATCH/DELETE

The field ID must be a valid UUID for a definition that exists and is accessible to your auth scope. Organisation users cannot see or modify partner-scoped definitions. Partner users cannot see or modify definitions belonging to organisations outside their access.