How to read and write custom field values at every level of the hierarchy via the Level.io v2 REST API.
All requests require the API key in the Authorization header — raw key, no prefix:
Authorization: your-api-key-here
Content-Type: application/json
Base URL: https://api.level.io/v2
Organisation (global default)
└── Group (override)
└── Child Group (override)
└── Device (override — highest priority)
Values cascade down. A device inherits its group's value unless a device-level override is set.
Before reading or writing values, you need the field's name (not id, not the cf_ reference).
GET /v2/custom_fields?limit=100
Response fields of interest:
| Property | Example | Notes |
|---|---|---|
id |
Z2lkOi8v... |
GID — needed for some reads |
name |
policy_meshcentral_meshid |
Use this for writes |
reference |
cf_policy_meshcentral_meshid |
Template variable — NOT for API writes |
Paginate with ?limit=100&starting_after=<last_id> if you have many fields.
This is the fallback value for all groups and devices that have no override.
GET /v2/custom_field_values?limit=100
Returns all fields where no entity override is set (org-level defaults).
PATCH /v2/custom_field_values
{
"custom_field_id": "Z2lkOi8v...",
"assigned_to_id": null,
"value": "your-value"
}
⚠️ Critical:PATCH /v2/custom_field_valueswith a group ID asassigned_to_idreturns HTTP 200 but silently discards the value. Always usePATCH /v2/groups/<id>instead.
⚠️ Critical:GET /v2/custom_field_values?assigned_to_id=<group_id>returns the inherited global default, not any group-level override. Always read viaGET /v2/groups/<id>.
GET /v2/groups/<group_id>
The response includes a custom_fields object with all explicit overrides set on that group:
{
"id": "Z2lkOi8v...",
"name": "ACME Corp",
"custom_fields": {
"policy_meshcentral_meshid": "abc123...",
"policy_chrome": "install"
}
}Fields not listed here are inherited from the parent group or org default.
PATCH /v2/groups/<group_id>
{
"custom_fields": {
"policy_meshcentral_meshid": "abc123..."
}
}
Use the field name as the key — not the cf_ reference and not the field ID.
GET /v2/groups?limit=100
Paginate with starting_after. Groups are what Level.io calls clients/organisations — there is no /v2/folders or /v2/organizations endpoint (both return 404).
GET /v2/custom_field_values?assigned_to_id=<device_id>&limit=100
Returns the effective value for that device — either the device override or the inherited value from its group chain. This endpoint works correctly for devices (unlike groups).
PATCH /v2/custom_field_values
{
"custom_field_id": "Z2lkOi8v...",
"assigned_to_id": "<device_id>",
"value": "your-value"
}
Use the field id (GID) here, not the name.
GET /v2/devices?limit=100
Key device properties: id, hostname, group_id, group_name, platform (e.g. "Windows", "Linux").
There is no bulk endpoint for this. You must query each group individually:
GET /v2/groups/<group_id>
Check whether the field appears in .custom_fields. If it does, that group has an explicit override. If not, it's inheriting from a parent or the org default.
At scale (many groups × many fields) this requires individual per-group queries. Rate limit is 100 calls/min — allow 600ms between calls.
| Goal | Endpoint | Method | Notes |
|---|---|---|---|
| Read org default | /v2/custom_field_values |
GET | Filter by custom_field_id client-side |
| Write org default | /v2/custom_field_values |
PATCH | assigned_to_id: null |
| Read group override | /v2/groups/<id> |
GET | Check .custom_fields |
| Write group override | /v2/groups/<id> |
PATCH | Body: {"custom_fields": {"name": "value"}} |
| Read device effective value | /v2/custom_field_values?assigned_to_id=<device_id> |
GET | Returns inherited or override |
| Write device override | /v2/custom_field_values |
PATCH | Use field GID + device assigned_to_id |
| List groups | /v2/groups |
GET | Paginate with limit + starting_after |
| List devices | /v2/devices |
GET | Paginate with limit + starting_after |
| List field definitions | /v2/custom_fields |
GET | Get field name and id |
| Mistake | Result | Fix |
|---|---|---|
PATCH /v2/custom_field_values with group assigned_to_id |
HTTP 200 but value not saved | Use PATCH /v2/groups/<id> |
GET /v2/custom_field_values?assigned_to_id=<group_id> |
Returns global default, not group override | Use GET /v2/groups/<id> |
Using cf_field_name as key in group PATCH body |
Field not found / silently ignored | Use field_name (no cf_ prefix) |
Using field id (GID) as key in group PATCH body |
Field not found | Use field name |
Calling /v2/folders or /v2/organizations |
404 | Use /v2/groups |
Adding Bearer to Authorization header |
401 | Raw key only, no prefix |