Documentation

Device Tags API

Read and replace the device category list and the system list that device tags are built from.

Every physical unit on a project can carry a device tag such as DISP-003, built from your company's tag pattern. The pattern draws on two lists: device categories, which classify catalog products (Display / DISP), and systems, which equipment lines belong to (Audio Visual / AV). This page covers the two lists. For how tags work in the app, see Device Tags.

Where the Other Device Tag Endpoints Are

TaskEndpointDocumented in
Set or clear a product's categoryPUT /api/v1/products/{id}/categoryProducts API
Set or clear the category on many productsPOST /api/v1/products/set-categoryProducts API
Set or clear a line's systemPUT /api/v1/projects/{projectId}/rooms/{roomId}/items/{itemId}/systemProjects API
Set or clear the system on many linesPOST /api/v1/projects/{projectId}/items/set-systemProjects API
Tag every untagged unit in a projectPOST /api/v1/projects/{projectId}/device-tags/assignProjects API
Read units with their tags; set a unit's tagGET …/items/{itemId}/units, PATCH …/items/{itemId}/units/{instanceId}Line Item Attributes API
Map an attribute key to a CAD attribute tagPUT /api/v1/line-item-attribute-keys/{id}Line Item Attributes API

The tag pattern, sequence digits and numbering mode are set on the Device Tags card in Global Config; they have no endpoint. The CAD block library, the CAD tag catalog and a project's CAD units are in the CAD Blocks API.

Rules for Both Lists

  • A name is trimmed, required, at most 60 characters, and unique in its list ignoring letter case.
  • An abbreviation is trimmed and upper-cased, then must be 1 to 8 letters or digits (A-Z, 0-9). It is unique in its list.
  • order is the row's 1-based position. A PUT sets it from the order of the items you send.
  • A PUT replaces the whole list in one save: an item with an id updates that row, an item without one creates a row, and every row you leave out is deleted. Nothing is saved when any item is refused.
  • Changing a name or an abbreviation affects only tags assigned afterwards. Existing tags never change.
  • The first read of a list that has no rows fills it with the default list. A PUT with an empty array therefore deletes every row, and the next GET returns the defaults.

GET /api/v1/cad-categories

Returns the device category list, in order.

Example Request

GET /api/v1/cad-categories
Authorization: Bearer YOUR_JWT_TOKEN

Response (200 - Success)

{
    "success": true,
    "data": [
        {
            "id": 7,
            "name": "Display",
            "abbreviation": "DISP",
            "isDrawable": true,
            "expectedOn": ["plan", "elevation"],
            "order": 1,
            "updatedAt": "2026-10-11T15:30:00Z",
            "cadBlocks": [
                { "view": "plan", "blockId": 9, "visibilityState": "TOP" }
            ]
        },
        {
            "id": 34,
            "name": "Cable",
            "abbreviation": "CBL",
            "isDrawable": false,
            "expectedOn": [],
            "order": 28,
            "updatedAt": "2026-10-11T15:30:00Z",
            "cadBlocks": []
        }
    ],
    "message": null,
    "timestamp": "2026-10-11T15:30:00Z"
}
FieldTypeDescription
idintegerThe category's ID. Products carry it as cadCategoryId.
namestringThe long name shown in dropdowns, for example Display
abbreviationstringThe short form that goes into tags, for example DISP
isDrawablebooleanfalse for items that are not drawn as devices, such as cable and labor. GET /api/v1/projects/{projectId}/cad/units leaves their units out unless asked; tags are assigned either way.
expectedOnarray of stringThe views the category's units are expected on, in view order: plan, ceiling, elevation, schematic. The project page counts a unit as not drawn while no linked drawing of such a view places it. Empty when the category is not checked.
orderintegerThe row's 1-based position in the list
updatedAtstring (ISO 8601)When the row last changed
cadBlocksarray of objectThe category's default CAD block for each view it sets, in view order: view, blockId and visibilityState (null for the block's default state). A product of the category uses it for any view the product sets no block of its own. Empty when the category has none. A block archived since it was chosen is still listed but no longer used.

PUT /api/v1/cad-categories

Replaces the whole device category list and returns the list as saved. A category you leave out is deleted, and the products that had it become unclassified; their existing device tags stay.

Request Body

FieldTypeRequiredDescription
categoriesarray of objectYesThe full list, in the order you want it. An empty array deletes every category.

Category Item

FieldTypeRequiredDescription
idintegerNoThe ID of the category to update. Leave it out to create a category.
namestringYesThe category name
abbreviationstringYesThe abbreviation used in tags
isDrawablebooleanNoDefaults to true
expectedOnarray of stringNoView names, any letter case; repeats are dropped. Leave the field out (or send null) to keep the category's views as they are; a new category then starts with none. An empty array clears them.
cadBlocksarray of objectNoThe category's default blocks, each { "view", "blockId", "visibilityState" } as in PUT /api/v1/products/{id}/cad-blocks, replacing the ones it has, a new category included. Leave the field out to keep the category's blocks as they are; an empty array clears them all.

Example Request

{
    "categories": [
        { "id": 7, "name": "Display", "abbreviation": "DISP", "expectedOn": ["plan", "elevation"], "cadBlocks": [ { "view": "plan", "blockId": 9, "visibilityState": "TOP" } ] },
        { "id": 34, "name": "Cable", "abbreviation": "CBL", "isDrawable": false },
        { "name": "Lectern", "abbreviation": "LECT" }
    ]
}

Response (200 - Success)

{
    "success": true,
    "data": [
        { "id": 7, "name": "Display", "abbreviation": "DISP", "isDrawable": true, "expectedOn": ["plan", "elevation"], "order": 1, "updatedAt": "2026-10-11T15:30:00Z", "cadBlocks": [ { "view": "plan", "blockId": 9, "visibilityState": "TOP" } ] },
        { "id": 34, "name": "Cable", "abbreviation": "CBL", "isDrawable": false, "expectedOn": [], "order": 2, "updatedAt": "2026-10-11T15:42:00Z", "cadBlocks": [] },
        { "id": 61, "name": "Lectern", "abbreviation": "LECT", "isDrawable": true, "expectedOn": [], "order": 3, "updatedAt": "2026-10-11T15:42:00Z", "cadBlocks": [] }
    ],
    "message": "Device categories saved",
    "timestamp": "2026-10-11T15:42:00Z"
}

Error Responses

StatusCodeDescription
400INVALID_INPUTcategories is missing or holds a null item, an id appears twice, or a name or abbreviation breaks a rule. The message names the row, for example Row 3: Abbreviation 'LECTERNS1' is not valid: use 1 to 8 letters or digits (A-Z, 0-9), such as DISP. A cadBlocks item that is null, has no view or an unknown one, has no blockId, or repeats a view is refused the same way, and so is an expectedOn name that is not a view: Row 1: expectedOn: 'bogus' is not a view. Use plan, ceiling, elevation or schematic.
400UNKNOWN_FIELDSA cadBlocks item names a field other than view, blockId and visibilityState
400BLOCK_ARCHIVED, VIEW_MISMATCH, INVALID_VISIBILITY_STATEA cadBlocks item breaks a block rule, as in PUT /api/v1/products/{id}/cad-blocks. The message names the row. Every item is checked before anything is saved, so nothing changes, neither the list nor any category's blocks.
403READ_ONLY_ACCESSThe caller has read-only access
404NOT_FOUNDAn id is not one of your categories
404BLOCK_NOT_FOUNDA cadBlocks item's blockId is not one of your blocks. Nothing is saved.
409DUPLICATE_CATEGORYTwo items share a name (ignoring letter case) or an abbreviation

GET /api/v1/cad-systems

Returns the system list, in order. The default list is Audio Visual / AV, Information Technology / IT and Security / SEC.

Example Request

GET /api/v1/cad-systems
Authorization: Bearer YOUR_JWT_TOKEN

Response (200 - Success)

{
    "success": true,
    "data": [
        { "id": 3, "name": "Audio Visual", "abbreviation": "AV", "order": 1, "updatedAt": "2026-10-11T15:30:00Z" },
        { "id": 4, "name": "Information Technology", "abbreviation": "IT", "order": 2, "updatedAt": "2026-10-11T15:30:00Z" },
        { "id": 5, "name": "Security", "abbreviation": "SEC", "order": 3, "updatedAt": "2026-10-11T15:30:00Z" }
    ],
    "message": null,
    "timestamp": "2026-10-11T15:30:00Z"
}

The fields are those of a category without isDrawable and expectedOn. Equipment lines carry the system's id as cadSystemId.


PUT /api/v1/cad-systems

Replaces the whole system list and returns the list as saved. A system you leave out is deleted, and every line that had it, in every version of every project, becomes unassigned; existing device tags stay.

Request Body

FieldTypeRequiredDescription
systemsarray of objectYesThe full list, in the order you want it. Each item takes id (leave it out to create a system), name and abbreviation. An empty array deletes every system.

Example Request

{
    "systems": [
        { "id": 3, "name": "Audio Visual", "abbreviation": "AV" },
        { "id": 5, "name": "Security", "abbreviation": "SEC" },
        { "name": "Lighting", "abbreviation": "LTG" }
    ]
}

Response (200 - Success)

{
    "success": true,
    "data": [
        { "id": 3, "name": "Audio Visual", "abbreviation": "AV", "order": 1, "updatedAt": "2026-10-11T15:30:00Z" },
        { "id": 5, "name": "Security", "abbreviation": "SEC", "order": 2, "updatedAt": "2026-10-11T15:42:00Z" },
        { "id": 9, "name": "Lighting", "abbreviation": "LTG", "order": 3, "updatedAt": "2026-10-11T15:42:00Z" }
    ],
    "message": "Systems saved",
    "timestamp": "2026-10-11T15:42:00Z"
}

Error Responses

StatusCodeDescription
400INVALID_INPUTsystems is missing or holds a null item, an id appears twice, or a name or abbreviation breaks a rule. The message names the row.
403READ_ONLY_ACCESSThe caller has read-only access
404NOT_FOUNDAn id is not one of your systems
409DUPLICATE_SYSTEMTwo items share a name (ignoring letter case) or an abbreviation

MCP clients reach the same lists with list_cad_categories and list_cad_systems; see the MCP Server page.