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.
| Operation | Request | Permission |
|---|---|---|
listElementCategories | GET /element-categories | view_properties |
listElementCatalog | GET /element-catalog | view_properties |
createElementCatalogEntry | POST /element-catalog | modify_properties |
updateElementCatalogEntry | PATCH /element-catalog/{id} | modify_properties |
deleteElementCatalogEntry | DELETE /element-catalog/{id} | modify_properties |
listPropertyElements | GET /property-elements | view_properties |
createPropertyElement | POST /property-elements | modify_properties |
updatePropertyElement | PATCH /property-elements/{id} | modify_properties |
deletePropertyElement | DELETE /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.
Categories
Section titled “Categories”GET /element-categories takes no parameters and returns every category as one unpaginated array, areas first and then items, each sorted by name.
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). Theirnameis a snake_case key such asliving_roomorhalf_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
kitchenhas a differentidin every organization. This operation is the only way to get one, and both create operations below require it. Look ids up bynameandlevelrather than hard-coding them. - Only
areaanditemcategories are returned. The legacysub_itemlevel is retired.
Catalog entries
Section titled “Catalog entries”A catalog entry is a template, not something placed anywhere. Linking a property element to it is optional.
Listing the catalog
Section titled “Listing the catalog”GET /element-catalog returns every entry as one unpaginated array, sorted by name. Pass categoryId to narrow it to one category.
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.
Creating an entry
Section titled “Creating an entry”POST /element-catalog requires name (up to 200 characters) and categoryId. The response is 201 with the new entry under data.
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 } }'namemust be unique within its category for the organization. A duplicate returns409 conflict_error, so readlistElementCatalogbefore retrying a create. The same name in a different category is fine.categoryIdfrom another organization returns404 not_found_error.description(up to 2,000 characters),makeandmodel(up to 200 each),notes(up to 5,000) andmetadata(a flat JSON object) are optional.sleepingCapacity(an integer, 0 or more) is how many people this type of element sleeps, for example2for 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,descriptionandnotesyou send are translated into the organization’s other languages automatically after the create.
Updating an entry
Section titled “Updating an entry”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.
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.
categoryIdindatais rejected with400 validation_error, like any other unknown field. - A rename onto a name its category already holds returns
409 conflict_error. metadatareplaces 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.
Deleting a catalog entry
Section titled “Deleting a catalog entry”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.
Property elements
Section titled “Property elements”The tree
Section titled “The tree”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.
Listing a property’s elements
Section titled “Listing a property’s elements”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).
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-…" }}conditionis the element’s newest condition check from a task: itsresult(pass,flag,failorna, typed as an open string), theanswerLabelin your language when there is a translation, andcheckedAt;resultandanswerLabelmay each benull. It isnullwhen the element has never been checked, and when the caller holds neitherview_tasksnormodify_tasks. Create and update responses always returnnull. An answer list withlogsElementConditionon 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.
levelis typed as an open string so a future level can still be read. Handle values other thanareaanditem.
Creating an element
Section titled “Creating an element”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:
# 1. The areacurl -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 parentInstanceIdcurl -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" }'levelisareaoritem.sub_itemis rejected with400 validation_error.parentInstanceIdis required for an item and must be an area; leave it out for an area. Getting either wrong isn’t caught by validation and returns500 internal_error, so checklevelandparentInstanceIdbefore sending. An item under another item returns403 authorization_error, even though your credential is fine.propertyIdis required for areas and items alike. For an item it must be the same property as its parent area, or the create returns400 validation_error.groupIdappears in the schema but is rejected with400 validation_error. This operation places elements on a property only.catalogEntryIdonly links the template.make,model,notesand the other text fields are not copied from it, so send them yourself if you want them on the element.sleepingCapacityis the exception: see below.quantity(an integer, 1 or more) defaults to1. Use it for identical items, for example2for a pair of nightstands. For rooms, create one area per room.sleepingCapacityoverrides the catalog entry’s value for this placement. Omit it to inherit.- A
categoryId,catalogEntryId,propertyIdorparentInstanceIdfrom another organization returns404 not_found_error, and so does a property outside an OAuth token’s property access. - The
name,descriptionandnotesyou send are translated into the organization’s other languages automatically.
Updating an element
Section titled “Updating an element”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.
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. lastServicedAtandwarrantyExpiresAtare ISO 8601 timestamps in UTC, such as2026-09-15T00:00:00Z. They come back with milliseconds.catalogEntryId: nullunlinks the template.sleepingCapacity: nulldrops the override so the catalog value applies again.metadatareplaces 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.
Deleting an element
Section titled “Deleting an element”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,completionorgeneralarea on a property can’t be deleted: the call returns403. 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.
Sleeping capacity
Section titled “Sleeping capacity”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.
How elements connect to tasks
Section titled “How elements connect to tasks”Property element ids are what the task operations take when work is about one specific thing:
createTaskacceptselementInstanceId, for example to file a repair against one dishwasher so it lands in that element’s history. It needs apropertyId. Elements shared by the property’s group are accepted too.updateTaskcan re-pointelementInstanceIdor clear it withnull. 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. Sendnullin the same call.listTasksfilters byelementInstanceId, which gives the repair history of one element. Combine it withstatusesto check whether a repair is already open.addTaskRequirement(POST /task-requirements) attaches a checklist row to one element of the task’s property withelementInstanceId. Instead,elementCategory(general,arrivalorcompletion) 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 fromlistElementCategories, makes the group repeat once for every matching element on the task’s property.targetLevel(areaoritem) andparentCategoryIdnarrow the match.targetLevelwithouttargetCategoryIdis rejected, and so isparentCategoryIdwithtargetLevel: "area". So the elements a property has decide which checklist rows a templated task gets.