Read Properties - /scan
The POST scan endpoint is used to retrieve property data from the backend database. The API is designed to be very efficient and to match the access pattern of large models with many elements. All read-access for property data is handled through this single endpoint, but options in the request payload allow for a variety of queries.
The first thing to understand is the structure of the Tandem data model. Imagine it as a sparse 3D Matrix, where rows are elements in the model, columns are attributes, and the third dimension is the history of values that property has had over time.
The columns (attributes) are grouped into families, each of which is depicted by a hard-coded character specification (shown below).
const ColumnFamilies = {
Status: "s",
Attributes: "p",
AttributeHashes: "h",
AccessControl: "c",
LMV: "0",
Standard: "n", // "common" properties for all elements
Refs: "l", // references to other elements (e.g., Type, Level, Space)
Xrefs: "x", // references to elements in other models
Source: "r", // properties that come from the source model files (e.g., Revit or IFC)
DtProperties: "z", // user-defined parameters
Tags: "t",
Systems: "m",
UserInfo: "u",
ChangeInfo: "c",
Virtual: "v"
};
Some column names within each family are hardcoded in the Tandem schema. For instance, n:n is the well-qualified name for the “Common” attribute “Name”. The following are the internal definitions for the built-in column names:
const ColumnNames = {
// LMV-isms
BoundingBox: "0", //family: LMV, data table
Fragment: "1", //family: LMV, data table
LmvModelRoot: "lmv", //family: LMV, data table
AecModelData: "aec", //family: LMV, data table
DocumentId: "docid", //family: Standard, meta table, the GUID of the Revit document originally used to populate this model
ForgeUrn: "urn", //family: Standard, meta table, the URN of the source Derivative Service model used to populate our model
Version: "v", //family: Status. Schema version used for this Digital Twin,
Deleted: "e", //family: Status
ODeleted: "!e", //family: Status, override
ElementFlags: "a", //family: Standard
UniformatClass: "u", //family: Standard, data table
OUniformatClass: "!u", //family: Standard, data table, override
Classification: "v", //family: Standard, data table (value) + family Status, meta table (scheme)
OClassification: "!v", //family: Standard, data table, override
CategoryId: "c", //family: Standard
FamilyPath: "f", //family: Standard, Family Type elements only.
Name: "n", //family: Standard
OName: "!n", //family: Standard, name override
SystemClass: "b", //family: Standard, data table, Revit System Classification as bitmask
OSystemClass: "!b", //family: Standard, data table, Revit System Classification as bitmask, override
Elevation: "el", //family: Standard, data table, Level elevation in model display units (as given by Revit)
OElevation: "!el",
CategoryName: "vc", //family: Virtual
Tolerance: "tol", // DEPRECATED. family: Standard, data table, MEPSystem mapping tolerance value
Config: "cfg", //family: Standard, data table. Element configuration (MEPSystems)
State: "s", //family: Status, data table, MEPSystem states (isDirty)
Parent: "p", //family: Refs
FamilyType: "t", //family: Refs
SubFamily: "s", //family: Refs
LmvDbId: "d", //family: Refs
Level: "l", //family: Refs
OLevel: "!l", //family: Refs, override
TopLevel: "m", //family: Refs, data table
Rooms: "r", //family: Refs, data table
Elements: "e", //family: Xrefs, data table. Holds an information about which elements are linked to the current element
UserID: "i", //family: UserInfo
ClientID: "c", //family: UserInfo
UserName: "n", //family: UserInfo
ChangeType: "t", //family: ChangeInfo
ChangeDesc: "d", //family: ChangeInfo
};
Some column names are dynamically generated, as is the case when a Tandem user creates and applies Parameters to an element via Classification. Any column that begins with the prefix z is user-defined. See Qualified Properties for information about how to look up the internal property identifier.
Let’s suppose you want to retrieve all properties associated with a given element, like in this screenshot from Tandem after selecting the Wall object:

If all properties are desired in the request, you would include something like the following:
{
"families": [
"n",
"l",
"x",
"r",
"z"
],
"includeHistory": false,
"keys": [
"ricOExIyRIyTke1_49OlKgAD-WQ"
]
}
In this request payload, we have specified that we want all the properties available that would normally show up in the Tandem UI. In the screenshot above, there are three highighted properties that all come from different column families.
NOTE: “family” in this context does NOT refer to a Revit Family. This family is a database term originating from NoSQL databases that denotes how the database is structured in groups of properties.
The response format for /scan
is an array of “things”. The first element is always a version, representing the format used (currently it is always v1). The array will have a return value for each corresponding key
that was specified. If the includeHistory
flag was true, each property returned may have multiple values with a timestamp.
The response from this call is below. The highlighted lines match the properties that are circled in the screenshot above:
[
"v1",
{
"k": "ricOExIyRIyTke1_49OlKgAD-WQ",
"n:a": [
0
],
"n:c": [
11
],
"n:n": [
"Basic Wall"
],
"n:u": [
"B2010156"
],
"n:!v": [
"EF_25_10"
],
"n:!n": [
"Test Wall"
],
"r:-gE": [
0
],
"r:1AE": [
0
],
"r:1AM": [
4145.164591038102
],
"r:3gE": [
0
],
"r:3wY": [
"BLDG 4 - EastWall - Ground to Roof"
],
"r:6AI": [
"Revit Walls"
],
"r:7gI": [
"Bearing"
],
"r:7gQ": [
""
],
"r:9gI": [
0
],
"r:9wY": [
"Up to level: T.O. ROOF BLDG 4"
],
"r:PA": [
"Finish Face: Exterior"
],
"r:UA": [
"None"
],
"r:Zw": [
"260452"
],
"r:_gI": [
-2000011
],
"r:eg": [
""
],
"r:iQY": [
"Rebar Cover 1 <0' - 1\">"
],
"r:kgI": [
0
],
"r:lgY": [
"Rebar Cover 1 <0' - 1\">"
],
"r:nQU": [
""
],
"r:ngI": [
1
],
"r:owI": [
"Vertical"
],
"r:qwM": [
1
],
"r:sAM": [
"Rebar Cover 1 <0' - 1\">"
],
"r:sAU": [
3454.3038258651127
],
"r:sgE": [
80.31693606751301
],
"r:tQE": [
0
],
"r:tQQ": [
"All-Ground Level"
],
"r:twE": [
0
],
"r:vAQ": [
""
],
"r:xQE": [
1
],
"r:xgQ": [
51.583333333333336
],
"r:yQU": [
"New Construction"
],
"z:5Ac": [
"value 9876"
],
"z:6gc": [
"Test Value 1"
],
"z:4Qc": [
12312
],
"z:3wc": [
"New DEV TEST"
],
"l:l": [
"kzxKBpO4EdOA-ADAT478MgAAAB4"
],
"l:m": [
"KEecOwAQRgC44ntF5eaV1QACU-Y"
],
"l:t": [
"eSelem67SMmRIHH6JgautgAD7RM"
],
"l:!l": [
"8KBjb_ZrSO6bBWAxWYViCgAKpgk"
]
}
]
Notice that there is a property returned for both n:n and n:!n. The majority of properties that come in from the source file (Revit, IFC, …) are read-only. However, there are a small number that allow an override that the user can set using the UI, or you can set programmatically. These properties are listed in the Column Names enumeration above and are indicated with a ! prefix.
If you also want the Type properties, you have to also include the keys
for the Type object of that particular wall (the Wall Family in Revit terms).
If we wanted ONLY the user-defined properties on the given element, we would just include the z family, as in the following example:
{
"families": [
"z"
],
"includeHistory": false,
"keys": [
"ricOExIyRIyTke1_49OlKgAD-WQ"
]
}
Here, you will see that we get a smaller set of data in response, restricted to only the user-defined properties:
[
"v1",
{
"k": "ricOExIyRIyTke1_49OlKgAD-WQ",
"z:5Ac": [
"value 9876"
],
"z:6gc": [
"Test Value 1"
],
"z:4Qc": [
12312
],
"z:3wc": [
"New DEV TEST"
]
}
]
If we change the includeHistory flag to true, you will see the response changes to include multiple values for each property, each one with a timestamp to indicate when the change was made.
[
"v1",
{
"k": "ricOExIyRIyTke1_49OlKgAD-WQ",
"z:5Ac": [
"value 9876",
1674160465908,
"value 1234",
1674159089299,
"MaxAsset_TBD",
1674158373580
],
"z:6gc": [
"Test Value 1",
1691516985971,
"New Duplicate Value 11112222111",
1688952724180,
"New Duplicate Value 1111222211",
1688952710548,
"New Duplicate Value 111122221",
1688952487215,
"New Duplicate Value 11112222",
1688751452438,
"New Duplicate Value 1111",
1680707416889,
"Test value",
1680628011953
],
"z:4Qc": [
12312,
1690764553011,
12344,
1690764314690,
null,
1688966718853
],
"z:3wc": [
"New DEV TEST",
1690531615945,
"New Duplicate Value DEV TEST",
1690351866645
]
}
]
Now let’s suppose we only want to retrieve a SINGLE user-defined property. If we know the “fully qualified” name, /scan
will only return that requested property (if it exists on that Element).
{
"qualifiedColumns": [
"z:5Ac"
],
"includeHistory": false,
"keys": [
"tOPQGwY_Sa-n8C80Y9x2xwAMnhA",
"ricOExIyRIyTke1_49OlKgAD-WQ"
]
}
In this example, we are specifying one or more qualifiedColumns
instead of a Column Family. In addition, we’ve asked for it to check the two elements in the keys
section of the payload. The result in this case is the following:
[
"v1",
{
"k": "ricOExIyRIyTke1_49OlKgAD-WQ",
"z:5Ac": [
"value 9876"
]
}
]
It found that property on one of the elements, but not the other.
If we want to find that propery on ALL elements where it exists, we can just leave off the keys
section of the payload, as follows:
{
"qualifiedColumns": [
"z:5Ac"
],
"includeHistory": false
}
[
"v1",
{
"k": "AKj0KDODTcyzbALnarH4wAAHlnk",
"z:5Ac": [
"2184"
]
},
{
"k": "ricOExIyRIyTke1_49OlKgAD-WQ",
"z:5Ac": [
"value 9876"
]
},
{
"k": "_0VkAOU5Te-hmAoPZfSahwAIb7Y",
"z:5Ac": [
"2191"
]
}
]
In this case, that property existed on three elements in the model. We could have passed in more qualifiedColumns
and it will return them all in a single query.
Looking up Display Names
Obviously, qualified names like z:5Ac or n:n are not suitable for display to the user. See Qualiied Properties for information on how to convert back and forth between internal, fully qualified names, and human-readable names.
NOTE: /scan
works on a per-model basis, so if you have 3 models loaded and have elements from each model you want the properties for, you need to call it 3 separate times.