Skip to content
Dashboard

Property elements

Elements describe what a property physically contains: its rooms and areas, and the appliances and fixtures inside them. They come in three layers. Categories are the organization’s taxonomy (bedroom, kitchen, dishwasher). Catalog entries are reusable make/model templates filed under a category, such as one TV model you install across many units. Property elements (element instances) are the actual things on one property, arranged as a tree of areas with items nested under them. Task checklists and repair tasks point at property elements. This guide covers the nine element operations. Every field is listed on the operation’s page in the API Reference.

OperationRequestPermission
listElementCategoriesGET /element-categoriesview_properties
listElementCatalogGET /element-catalogview_properties
createElementCatalogEntryPOST /element-catalogmodify_properties
updateElementCatalogEntryPATCH /element-catalog/{id}modify_properties
deleteElementCatalogEntryDELETE /element-catalog/{id}modify_properties
listPropertyElementsGET /property-elementsview_properties
createPropertyElementPOST /property-elementsmodify_properties
updatePropertyElementPATCH /property-elements/{id}modify_properties
deletePropertyElementDELETE /property-elements/{id}modify_properties

Paths are relative to your region’s base URL, for example https://api-us.suiteop.com/api/v1. See Authentication for permissions.

Catalog entries and property elements can be deleted permanently. The API has no read-one operation (the lists already return every field), and no way to create, rename or delete a category.

GET /element-categories takes no parameters and returns every category as one unpaginated array, areas first and then items, each sorted by name.

Terminal window
curl "https://api-us.suiteop.com/api/v1/element-categories" \
-H "Authorization: Bearer sk_live_your_key_here"
{
"data": [
{
"id": "3c9a1f7e-2b4d-4e8a-9f10-5a6b7c8d9e01",
"name": "kitchen",
"level": "area",
"icon": null,
"isSystem": true,
"parentCategoryId": null
},
{
"id": "8e2d4b6a-1c3f-4a5b-8d7e-9f0a1b2c3d4e",
"name": "dishwasher",
"level": "item",
"icon": null,
"isSystem": true,
"parentCategoryId": null
}
],
"meta": { "requestId": "3f1c9a52-…" }
}
  • Every organization is seeded with a fixed set of system categories (isSystem: true). Their name is a snake_case key such as living_room or half_bathroom, not a display label. Custom categories added in the app carry the name as it was typed.
  • Category ids are generated per organization, so the same kitchen has a different id in every organization. This operation is the only way to get one, and both create operations below require it. Look ids up by name and level rather than hard-coding them.
  • Only area and item categories are returned. The legacy sub_item level is retired.

A catalog entry is a template, not something placed anywhere. Linking a property element to it is optional.

GET /element-catalog returns every entry as one unpaginated array, sorted by name. Pass categoryId to narrow it to one category.

Terminal window
curl "https://api-us.suiteop.com/api/v1/element-catalog?categoryId=8e2d4b6a-1c3f-4a5b-8d7e-9f0a1b2c3d4e" \
-H "Authorization: Bearer sk_live_your_key_here"

Each entry carries id, name, description, categoryId, make, model, sleepingCapacity, notes and metadata.

POST /element-catalog requires name (up to 200 characters) and categoryId. The response is 201 with the new entry under data.

Terminal window
curl -X POST https://api-us.suiteop.com/api/v1/element-catalog \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Idempotency-Key: 6f1d2c3b-4a5e-4f60-8a7b-9c0d1e2f3a4b" \
-H "Content-Type: application/json" \
-d '{
"name": "Bosch 300 Series dishwasher",
"categoryId": "8e2d4b6a-1c3f-4a5b-8d7e-9f0a1b2c3d4e",
"make": "Bosch",
"model": "SHE53C85N",
"notes": "Clean the filter monthly.",
"metadata": { "warrantyYears": 2 }
}'
  • name must be unique within its category for the organization. A duplicate returns 409 conflict_error, so read listElementCatalog before retrying a create. The same name in a different category is fine.
  • categoryId from another organization returns 404 not_found_error.
  • description (up to 2,000 characters), make and model (up to 200 each), notes (up to 5,000) and metadata (a flat JSON object) are optional.
  • sleepingCapacity (an integer, 0 or more) is how many people this type of element sleeps, for example 2 for a queen bed. Placed elements linked to the entry inherit it unless they override it. Omit it for anything that isn’t a bed.
  • The name, description and notes you send are translated into the organization’s other languages automatically after the create.

PATCH /element-catalog/{id} takes a data object with any of name, description, make, model, sleepingCapacity, notes and metadata. Send null to clear any of them except name.

Terminal window
curl -X PATCH https://api-us.suiteop.com/api/v1/element-catalog/5b7c9d1e-2f3a-4b5c-8d6e-7f8091a2b3c4 \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"data": {"model": "SHE53C85N-2", "notes": null}}'
  • The category can’t be changed. categoryId in data is rejected with 400 validation_error, like any other unknown field.
  • A rename onto a name its category already holds returns 409 conflict_error.
  • metadata replaces the whole stored object. Merge on your side before sending it.
  • Resending a text field unchanged doesn’t re-translate it. Only text that actually changes is re-translated, which replaces the other languages’ copies of that field.

DELETE /element-catalog/{id} deletes the entry permanently and returns {"success": true}. Property elements linked to it stay where they are, with catalogEntryId cleared. The attachments the entry owns, such as manuals and photos, are deleted with it, so those elements stop showing them. There is no undo: read the entry with listElementCatalog first if you may need to recreate it.

A property’s elements form a two-level tree:

  • area: a room or zone, such as a kitchen, a bedroom or the pool. Areas are the roots and never have a parent.
  • item: something inside an area, such as a dishwasher or a smoke alarm. Every item has exactly one area as its parent, on the same property.

A third level, sub_item, has been retired. It can’t be created, and existing sub-items were converted into supply lines on their parent area or item, so none appear in the tree.

A new property starts with an arrival, a completion and a general area, which checklists use for work that isn’t about one room, plus one area per bedroom and full bathroom it was created with, a half_bathroom area for a half bath, and any areas picked when it was created. Older properties can lack the three mandatory areas.

GET /property-elements?propertyId=… returns the property’s areas as an unpaginated array. Each area nests its items under childInstances. Each item also carries a childInstances array, which is always empty (it’s kept for shape compatibility).

Terminal window
curl "https://api-us.suiteop.com/api/v1/property-elements?propertyId=9a3d7e40-5b6c-4d7e-8f90-1a2b3c4d5e6f" \
-H "Authorization: Bearer sk_live_your_key_here"
{
"data": [
{
"id": "0d4e6f80-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"name": "Kitchen",
"level": "area",
"categoryId": "3c9a1f7e-2b4d-4e8a-9f10-5a6b7c8d9e01",
"catalogEntryId": null,
"propertyId": "9a3d7e40-5b6c-4d7e-8f90-1a2b3c4d5e6f",
"parentInstanceId": null,
"quantity": 1,
"sleepingCapacity": null,
"serialNumber": null,
"make": null,
"model": null,
"description": null,
"notes": null,
"lastServicedAt": null,
"warrantyExpiresAt": null,
"metadata": null,
"condition": null,
"childInstances": [
{
"id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
"name": "Dishwasher",
"level": "item",
"categoryId": "8e2d4b6a-1c3f-4a5b-8d7e-9f0a1b2c3d4e",
"catalogEntryId": "5b7c9d1e-2f3a-4b5c-8d6e-7f8091a2b3c4",
"propertyId": "9a3d7e40-5b6c-4d7e-8f90-1a2b3c4d5e6f",
"parentInstanceId": "0d4e6f80-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"quantity": 1,
"sleepingCapacity": null,
"serialNumber": "FD9601234567",
"make": "Bosch",
"model": "SHE53C85N",
"description": null,
"notes": null,
"lastServicedAt": "2026-06-01T00:00:00.000Z",
"warrantyExpiresAt": "2027-06-01T00:00:00.000Z",
"metadata": null,
"condition": {
"result": "flag",
"answerLabel": "Worn",
"checkedAt": "2026-09-28T14:05:00.000Z"
},
"childInstances": []
}
]
}
],
"meta": { "requestId": "3f1c9a52-…" }
}
  • condition is the element’s newest condition check from a task: its result (pass, flag, fail or na, typed as an open string), the answerLabel in your language when there is a translation, and checkedAt; result and answerLabel may each be null. It is null when the element has never been checked, and when the caller holds neither view_tasks nor modify_tasks. Create and update responses always return null. An answer list with logsElementCondition on records its answers as checks; see Answer lists.
  • Order is display order: siblings appear in the order they were created. There is no reorder operation.
  • An unknown property, one in another organization, or one outside an OAuth token’s property access returns an empty array, not 404. An empty result doesn’t prove the property exists.
  • Only elements filed on the property itself are listed. Elements an organization shares at the property-group level don’t appear here, and the API has no operation that lists them.
  • level is typed as an open string so a future level can still be read. Handle values other than area and item.

POST /property-elements requires name, categoryId, level and propertyId. The reference marks propertyId optional, but a create without it is rejected with 400 validation_error. The response is 201 with the new element under data, without childInstances. Create an area first, then its items:

Terminal window
# 1. The area
curl -X POST https://api-us.suiteop.com/api/v1/property-elements \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Idempotency-Key: 2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d" \
-H "Content-Type: application/json" \
-d '{
"name": "Kitchen",
"level": "area",
"categoryId": "3c9a1f7e-2b4d-4e8a-9f10-5a6b7c8d9e01",
"propertyId": "9a3d7e40-5b6c-4d7e-8f90-1a2b3c4d5e6f"
}'
# 2. An item in it, using the area's data.id as parentInstanceId
curl -X POST https://api-us.suiteop.com/api/v1/property-elements \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Idempotency-Key: 3b4c5d6e-7f8a-4b9c-8d0e-2f3a4b5c6d7e" \
-H "Content-Type: application/json" \
-d '{
"name": "Dishwasher",
"level": "item",
"categoryId": "8e2d4b6a-1c3f-4a5b-8d7e-9f0a1b2c3d4e",
"propertyId": "9a3d7e40-5b6c-4d7e-8f90-1a2b3c4d5e6f",
"parentInstanceId": "0d4e6f80-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"catalogEntryId": "5b7c9d1e-2f3a-4b5c-8d6e-7f8091a2b3c4",
"make": "Bosch",
"model": "SHE53C85N",
"serialNumber": "FD9601234567"
}'
  • level is area or item. sub_item is rejected with 400 validation_error.
  • parentInstanceId is required for an item and must be an area; leave it out for an area. Getting either wrong isn’t caught by validation and returns 500 internal_error, so check level and parentInstanceId before sending. An item under another item returns 403 authorization_error, even though your credential is fine.
  • propertyId is required for areas and items alike. For an item it must be the same property as its parent area, or the create returns 400 validation_error.
  • groupId appears in the schema but is rejected with 400 validation_error. This operation places elements on a property only.
  • catalogEntryId only links the template. make, model, notes and the other text fields are not copied from it, so send them yourself if you want them on the element. sleepingCapacity is the exception: see below.
  • quantity (an integer, 1 or more) defaults to 1. Use it for identical items, for example 2 for a pair of nightstands. For rooms, create one area per room.
  • sleepingCapacity overrides the catalog entry’s value for this placement. Omit it to inherit.
  • A categoryId, catalogEntryId, propertyId or parentInstanceId from another organization returns 404 not_found_error, and so does a property outside an OAuth token’s property access.
  • The name, description and notes you send are translated into the organization’s other languages automatically.

PATCH /property-elements/{id} takes a data object with any of name, description, catalogEntryId, quantity, sleepingCapacity, serialNumber, make, model, lastServicedAt, warrantyExpiresAt, notes and metadata. Send null to clear any of them except name and quantity.

Terminal window
curl -X PATCH https://api-us.suiteop.com/api/v1/property-elements/7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"data": {"lastServicedAt": "2026-09-15T00:00:00Z", "notes": "Replaced drain pump."}}'
  • An element can’t be moved to another property, category or parent, and its level can’t change. Those fields are rejected with 400 validation_error. To move something, create a new element where it belongs.
  • lastServicedAt and warrantyExpiresAt are ISO 8601 timestamps in UTC, such as 2026-09-15T00:00:00Z. They come back with milliseconds.
  • catalogEntryId: null unlinks the template. sleepingCapacity: null drops the override so the catalog value applies again.
  • metadata replaces the whole stored object, and unchanged text isn’t re-translated, as for catalog entries.
  • An element in another organization, or on a property outside an OAuth token’s access, returns 404 not_found_error.

DELETE /property-elements/{id} deletes the element permanently and returns {"success": true}. It also deletes every item nested under it, and the attachments and inventory items recorded against each deleted element. Tasks, task checklist rows and scopes that pointed at a deleted element keep existing but lose that link. There is no undo: read the tree with listPropertyElements first if you may need to recreate it.

  • The last arrival, completion or general area on a property can’t be deleted: the call returns 403. A duplicate of one of those areas can go while another remains.
  • If the element moves to another category, property or level while the delete runs, the call returns 409. Read it again and retry.

A property’s sleeping total is derived from its elements: each element counts its own sleepingCapacity, or its catalog entry’s when its own is null, or 0, multiplied by quantity. It doesn’t change the property’s maxGuests.

Property element ids are what the task operations take when work is about one specific thing:

  • createTask accepts elementInstanceId, for example to file a repair against one dishwasher so it lands in that element’s history. It needs a propertyId. Elements shared by the property’s group are accepted too.
  • updateTask can re-point elementInstanceId or clear it with null. It is checked against the property the task ends up on, so moving a task to another property while it still points at the old property’s element is rejected. Send null in the same call.
  • listTasks filters by elementInstanceId, which gives the repair history of one element. Combine it with statuses to check whether a repair is already open.
  • addTaskRequirement (POST /task-requirements) attaches a checklist row to one element of the task’s property with elementInstanceId. Instead, elementCategory (general, arrival or completion) targets that property’s mandatory area and creates it if it is missing.
  • Template requirement groups (addTemplateGroup, POST /task-template-groups) target categories, not elements. targetCategoryId, a category id from listElementCategories, makes the group repeat once for every matching element on the task’s property. targetLevel (area or item) and parentCategoryId narrow the match. targetLevel without targetCategoryId is rejected, and so is parentCategoryId with targetLevel: "area". So the elements a property has decide which checklist rows a templated task gets.