Docs Portal
Documentation
API ReferenceConsole

Connector Entities

How have entities replaced place mapping?

If you've configured connectors before, you'll remember place mappings: a hierarchical mapping table where you matched source-system rows to a Site → Building → Floor → Space → Thing tree, with a separate "provision" step and template-based mapping import. Entities replaces all of that.

What's changed:

Old way (place mappings)New way (Entities)
A flattened nested Site→Building→Floor→Space tree you filled inA flat table (Grid), one screen per entity type
Separate "provision" step, then "map" stepOne screen, one flow — Import, then Save
Mapping a Space to a Floor was a separate column/targetThe Space simply has a Floor cell you pick
CSV import for bulk changesRules written as a TypeScript function (optional)

One screen per "source type." A connector groups its data into source types — the names the source system uses for its own things (for example Airthings exposes building, space, and thing). Each source type gets its own section in the connector's settings, and each section is its own Entities screen with its own Grid and Rules. So a customer might see a Buildings screen, a Spaces screen, and a Devices screen, and work through them one at a time.

During a migration you may see both. A connector that's being moved from the old model to Entities can temporarily show the old mapping table and a red "Entities Migration" section. Migration is a one-time rename that keeps all existing data and history intact. Documentation on migration to be released soon.

What do Entities look like?

Two tabs at the top:

  • Grid — the table of entities (this is where most work happens).
  • Rules — the optional merge-rules editor

Two buttons at the top-right:

  • Import — pull the current list from the source system.
  • Save — write your choices into Mapped.

Above the Grid there's a smart search bar which can narrow what an Import pulls in. Depending on the connector, there may be filter fields (for example a Site picker or a sensor-type selector). Some connectors have no filters. Previous filter selections are saved between imports.

When no entities have been imported, an empty Grid at the bottom reads "No entities found. Click Import to load entities from the connector."

What happens when you Import?

Screenshot of the Course dog Buildings tab with no rows selected in the Grid view.

Import fetches the current, live list of entities from the source system and lays them out as rows in the Grid. Each row is one entity — one building, one room, one sensor.

On import, for each row the connector provides:

  • A name and other typed values (floor level, space code, address, which floor a room is on, …). These become the cells you can edit.
  • Read-only reference columns on the right (greyed, italic) — internal IDs and any extra context the connector attached. These are for lookup/troubleshooting; you can't edit them, but you can copy any cell by hovering it and clicking the copy button that appears.
  • A suggested default value for each editable cell (for example the name as the source system spells it).

Nothing is written to Mapped yet. Import only loads and showsSave is what commits.

Import reflects the source system exactly. The list you get is the complete current state of that source type. If something exists in the source, it shows up; if it doesn't, it won't. That matters a lot for re-imports — see the next section.

What changes are shown across repeated imports?

Import isn't a one-time action. A customer can click Import again whenever the source system changes (new rooms added, a sensor decommissioned, a name corrected). When they do, the screen compares the fresh import against what's currently saved in Mapped and highlights the differences.

You'll see a change banner / count summarizing the delta, using three categories:

  • Added — entities in the new import that weren't saved before (brand-new rows).
  • Updated — entities that already existed but whose values changed.
  • Deleted — entities that were saved before but are no longer reported by the source system.

Rows the source stopped reporting

An entity that the source system no longer returns is shown struck through (a line through the whole row) in red, its checkbox unticked and disabled, and its cells read-only. The next Save will remove it from Mapped.

These "gone" rows can't be kept. If the source system dropped an entity, nobody — not the customer, not a merge rule — can re-select it to keep it. The only way to keep it is for it to reappear in the source system and be imported again. This is deliberate: Mapped mirrors the source.

Your manual edits survive re-imports

Good news for customers: manual edits they made to a cell are remembered across re-imports. If a customer renamed a room and saved it, a later Import won't silently overwrite that name back to the source's version. (Two per-field flags — whether a cell is required and whether it must be unique — are refreshed on every import, because those are controlled by the connector, not the customer.)

Counts of any changes made sit at the bottom-left of the Grid:

Count indicatorColor
12 SELECTEDblue
3 ADDEDgreen
1 UPDATEDamber
2 DELETEDred

Only counts above zero appear. The bottom-right shows how much is loaded, e.g. 200 of 1450. Click any of these counts to filter the Grid down to those rows. For example, clicking 2 Deleted filters the Grid down to exactly those two rows. It is the fastest way to answer "what is about to be deleted?" The same counts appear in the Review Changes dialog, and clicking one there closes the dialog and applies the filter.

What happens when you edit cells?

The Grid is editable. A customer can double-click into most cells and change the value — rename a space, set a floor level, pick which floor a room belongs to, and so on. Each column uses an editor suited to its type (a text box for a name, a number for a floor level, a searchable picker for "which floor").

The "which floor / which building" picker (entity references)

Some cells don't hold plain text — they hold a link to another entity. For example, a Space has a "Floor" cell that points at the Floor it sits on. Editing that cell opens a tree picker of eligible places, and the customer selects one.

  • The picker only lets you choose valid targets for that link (a Floor cell only lists floors).
  • For large buildings, the picker shows the first chunk of results and says "Showing 500 of N — search to narrow." Keep scrolling to view more, or use the search bar to find specific records.

Cell color tells you where a value came from

This is the single most useful thing to teach a customer. Every cell is color-coded by its source:

What you seeWhat it means
Amber borderUnsaved edit — save to apply.
Dark-blue border, read-onlySet by a rule. Locked to manual edits.
Greyed textDefault value (placeholder)
No special stylingValue from the Graph.
Red borderDuplicate value in a unique field — must be unique to save.

There's a legend behind the little help icon on the checkbox column header that explains these colors. If two apply at once, red wins over blue, blue wins over amber, amber wins over grey.

Required and unique fields

  • Required fields are marked with an asterisk in the column header. If a required cell is empty, that row's checkbox is disabled — you can't include the entity until the field has a value. Hovering the checkbox shows which fields are missing.
  • Unique fields must not repeat across rows. If two rows share the same value in a unique column, both offending cells turn red and their checkboxes are disabled until the customer fixes one. (Blank values never count as duplicates.)

The checkbox = include or remove

The first column of every row is a checkbox:

  • Ticked = include this entity in Mapped (it will be created/updated on Save).
  • Unticking a row that's already saved = remove that entity from Mapped on Save.
  • A checkbox can be disabled because a required field is empty, a unique-field conflict exists, the source stopped reporting the row, or a merge rule controls the selection (see next document).

What happens when you click Save?

Save is the commit. It takes everything the customer has selected and edited and writes it to Mapped in one action.

The "Review Changes" confirmation

Clicking Save opens a Review Changes dialog before anything is written. It shows:

  • A "What are the changes for?" note field — a short reason the customer can type, for their own audit trail. The description is required to submit the change.
  • A summary of the change counts (added / updated / deleted).
  • If the customer only edited merge rules and touched no rows, it says "No entity changes — this saves your merge rule only."

The customer clicks Submit to proceed (or Cancel to back out).

What Save actually does

On Submit, Mapped processes the whole batch at once:

  • Selected (ticked) rows are created or updated in the graph, with the values shown in their cells.
  • Removed rows (unticked, or struck-through because the source dropped them) are deleted from the graph.

After a successful Save, the freshly edited cells become "settled" (no more amber borders), and those entities are now live in Mapped — visible to the rest of the platform.

If a Save fails, the Save button turns amber and permanently displays: "Latest merge failed. Click here to try again." Clicking retries straight away, and no changes are lost.

Save is per-screen. Each source type (Buildings, Spaces, Devices…) has its own Import and its own Save. A customer works through and saves each screen.

Quick reference — the whole flow

1 . Open the connector → open a source-type section (e.g. Spaces).

2 . (Optional) set any filters above the Grid.

3 . Click Import → rows load into the Grid.

4 . Review the change counts if this isn't the first import.

5 . Tick the rows to include; double-click to edit cells as needed (watch the colors).

6 . (Optional) use the Rules tab to auto-fill — read Merge Rules for more

7 . Click SaveReview ChangesSubmit.

8 . Repeat for the next source-type section.

Merge Rules

Merge rules are the advanced configuration options for importing data into the Mapped platform from a connector. They let you automate what would otherwise be manual row-by-row work in the Grid: instead of ticking and editing hundreds of entities by hand, you write a small piece of logic that runs once for every imported entity and fills in values and/or ticks checkboxes automatically.

Merge rules are written in code (TypeScript), but you don't need to be a developer to use them. However you should be comfortable reading and editing a short function or working with an AI agent that can. The Rules editor helps you: it autocompletes, checks your syntax as you type, and shows you exactly which fields exist for the connector you're configuring.

What's a merge rule?

A merge rule is a single function that the platform runs against one entity at a time, for every row in the Grid, every time you Import or preview. For each entity, the rule can:

  • Read facts about that entity — its source-system ID, extra context the connector attached, its current field values, and fixed identifiers (organisation, connector).
  • Set typed field values on it — force the name, set a floor level, point it at a floor.
  • Set its selection — tick or untick the checkbox to include or exclude it.

Whatever the rule sets shows up in the Grid as rule-owned cells (dark-blue border, read-only) and, where the rule sets selection, a rule-locked checkbox (disabled). Everything the rule doesn't touch is left to the customer's manual edits and the source defaults.

Why use them? Common jobs:

  • "Automatically include every room whose code starts with L (real leasable space) and skip the rest."
  • "Set every device's name from its serial number when the source didn't provide one."
  • "Attach every sensor with floor context 12 to our Floor 12 entity."

A rule applies one decision across thousands of rows, consistently, and re-applies automatically on every future import. A rule can point one entity at another by giving one of three things: the target's mapping key, its Mapped id, or its identity. For example:

Copy
1
entity.setField('floor', { type: 'FLOOR', mappingKey: 'msrc://…' })

If the field only accepts certain kinds of target (a Floor cell only accepts floors), it rejects anything else as you type — the same restriction the Grid picker enforces.

What's in the Rules tab?

Screen shot of the Rules tab

Each Entities screen has a Rules tab next to the Grid tab. It's a code editor (Monaco — the same engine as VS Code) with:

  • A rules.ts tab — the editable rule.
  • A read-only types.d.ts tab — an auto-generated reference showing exactly which fields and context this connector's entities have. This is your cheat sheet: it gives you autocomplete and red underlines for typos, and it's the definitive answer to "what can I read and set here?"
  • A status bar at the bottom that reads "TypeScript valid." when the rule is well-formed, or shows the first error. A rule with errors shouldn't be relied on, but they can be saved so you save your work.
  • Format (tidy the code) and Reset (return to the starter template) buttons.

The editor opens on this starter template, which does nothing until you fill it in:

Copy
1
2
3
4
5
6
7
// Merge rules — runs once per imported entity.
export default function rules(entity: RulesEntity): RulesEntity {
  // Example:
  //   entity.setField('name', 'Renamed')
  //   entity.setSelected(true)
  return entity
}

Always check the types.d.ts tab first. It's generated from the real entity for that source type, so it lists the actual field slugs (name, floorLevel, floor, …) and context keys the connector emits. Don't guess names — read them off that tab.

Screenshot of the types.d.ts tab

How do I read a rule?

A rule is just a function with a body. Read it as a list of "for this entity, do X." The building blocks are a handful of entity.* calls, split into reads and writes.

Reads — what a rule can look at

CallWhat it gives you
entity.getRefId()The entity's ID in the source system.
entity.getContext('key')Extra data the connector attached (e.g. a wing, a floor number, a status flag). Keys are listed in types.d.ts.
entity.getField('name')The entity's current value for a field (default, or a prior manual edit).
entity.getMappedId()The entity's existing Mapped ID, if it's already saved.
entity.getOrgId() / entity.getConnectorId() / entity.getConnectorTypeId()Fixed identifiers for the org and connector — occasionally useful for conditions.

Review the types.d.ts tab for a full list of read calls.

Writes — what a rule can change

CallWhat it does
entity.setField('slug', value)Sets a typed field value. The cell becomes rule-owned (dark-blue, read-only).
entity.setSelected(true | false)Ticks (include) or unticks (exclude) the checkbox. The checkbox becomes rule-locked.

A rule always returns the entity at the end (return entity) so the platform can apply the changes.

Worked example — reading it line by line

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
export default function rules(entity: RulesEntity): RulesEntity {
  // 1. Only include "real" leasable rooms — skip service closets, risers, etc.
  const code = entity.getField('spaceCode')          // read the room code, e.g. "L12-204"
  const isLeasable = typeof code === 'string' && code.startsWith('L')
  entity.setSelected(isLeasable)                       // tick it only if it's leasable

  // 2. If the source didn't give this room a name, fall back to its code.
  if (!entity.getField('name') && code) {
    entity.setField('name', code)                      // set the name to the code
  }

  return entity                                        // hand the entity back
}

Read as: "Tick every room whose code starts with L; for any of those missing a name, use its code as the name; leave everything else alone." That single rule then applies to every row on every import.

How do I write a rule?

The workflow:

  1. Open the source-type section (e.g. Spaces) and click Import at least once, so there are entities to work against and the reference types are generated.
  2. Go to the Rules tab and open types.d.ts to see the available fields and context keys.
  3. In rules.ts, write your logic using the reads/writes above. Start small — a single setSelected is a complete, useful rule.
  4. Watch the status bar: it should say "TypeScript valid." Fix any underlined errors.
  5. Switch back to the Grid to preview the effect — rule-set cells turn dark-blue and rule-locked checkboxes become disabled.
  6. Click Save. The rule is saved and will re-run on every future import.

Patterns customers use most

Include or exclude by a condition:

Copy
1
2
3
4
5
export default function rules(entity: RulesEntity): RulesEntity {
  // Include only occupancy sensors; leave everything else unticked.
  entity.setSelected(entity.getContext('deviceKind') === 'occupancy')
  return entity
}

Fill a missing value:

Copy
1
2
3
4
5
6
7
export default function rules(entity: RulesEntity): RulesEntity {
  // Give unnamed devices a name based on their source ID.
  if (!entity.getField('name')) {
    entity.setField('name', **Device ${entity.getRefId()}**)
  }
  return entity
}

Combine reads:

Copy
1
2
3
4
5
6
7
8
9
10
11
export default function rules(entity: RulesEntity): RulesEntity {
  const floor = entity.getContext('floorNumber')
  // Only bring in entities that report a floor, and stamp it into the name.
  if (floor != null) {
    entity.setField('name', **${entity.getField('name')} (Floor ${floor})**)
    entity.setSelected(true)
  } else {
    entity.setSelected(false)
  }
  return entity
}

Good habits

  • Only touch what you mean to. Anything the rule doesn't set stays under the customer's control. A rule that only calls setSelected never disturbs any names.
  • Prefer clear conditions over cleverness. Rules are read by other people (including future you and CS). Simple if checks on context/fields are ideal.
  • Let the reference types guide you. If types.d.ts doesn't list a field or context key, it isn't available — don't invent one.
  • Rules run every time the Grid loads or refreshes or Saves, so they keep decisions consistent as the source changes. A new room that matches the rule is handled automatically next time.

How do manual edits and Rules interact?

This is the question customers ask most, so it gets its own section. Every cell has a winning value, chosen from up to four sources in this priority order (highest wins):

1 . A merge rule — wins outright, locks the cell.

2 . An edit made in this session — typed but not saved yet.

3 . An edit saved earlier — survives re-imports.

4 . The value currently live in Mappedmissing from the list above

5 . The source system's default.

In plain language:

  • A rule overrides manual edits for the fields it touches. If a rule sets name, you can't hand-edit the name while that rule is in place — the cell is locked. To edit it by hand, change the rule so it stops setting that field.
  • A rule only owns the fields it sets. Every other cell still respects manual edits. A rule that sets only floor leaves name fully editable.
  • Manual edits beat defaults. For any field no rule controls, your typed value wins over the source default — and survives re-imports.
  • Selection works the same way. If a rule calls setSelected, the checkbox is rule-locked and you can't click to override it. If the rule doesn't touch selection, you control the checkbox normally.

Two hard gates that override even a rule

These protect data integrity and win over everything, including a rule:

  • Required-but-empty — a rule can't include an entity that's missing a required value.
  • Unique-field conflict (red cell) — blocks selection until the duplicate is resolved.

Rule of thumb: rules are for decisions you want made the same way every time; manual edits are for one-off corrections. If a cell is locked dark-blue, a rule owns it — change the rule, not the cell.

What happens when a Rule is Saved?

  • The rule itself is saved alongside the merge, so it persists and re-runs on future imports.
  • If you changed only the rule (no rows), the Review Changes dialog shows "No entity changes — this saves your merge rule only." Save still works; it just stores the rule.
  • If you changed rows and the rule, Save does both.

So you can iterate: tweak the rule → SaveImport later → the rule re-runs automatically.

Example: Setting Building Names and Postal Addresses with Grid Rules

If you maintain a reference of building identifiers, building names, and postal addresses, you can use a Grid Rule to automatically populate those values during import. The rule matches each building using its source-system Ref ID, then applies the correct name and postal address automatically. Using the building Ref ID is recommended because it is generally stable even if the building name changes over time.

Example Reference

Building Ref IDCanonical Building NamePostal Address
<building-id-1>Example Campus Building A100 Main Street, Springfield, MA 01103
<building-id-2>Example Campus Building B200 Main Street, Springfield, MA 01103
<building-id-3>Example Campus Building C300 Main Street, Springfield, MA 01103

Create the Lookup Table

Create an object that maps each building Ref ID to its canonical name and postal address.

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const BUILDINGS: Record<
  string,
  {
    name: string
    postalAddress: string
  }
> = {
  '<building-id-1>': {
    name: 'Example Campus Building A',
    postalAddress: '100 Main Street, Springfield, MA 01103',
  },

  '<building-id-2>': {
    name: 'Example Campus Building B',
    postalAddress: '200 Main Street, Springfield, MA 01103',
  },

  '<building-id-3>': {
    name: 'Example Campus Building C',
    postalAddress: '300 Main Street, Springfield, MA 01103',
  },
}

Apply the Values

The rule looks up the imported building by Ref ID. If a matching entry exists, it applies the canonical name and postal address and selects the building.

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default function rules(entity: RulesEntity): RulesEntity {
  const building = BUILDINGS[entity.getRefId()]

  // Unknown buildings are left untouched so they appear in the
  // Grid for review instead of being assigned incorrect values.
  if (!building) {
    return entity
  }

  entity.setField('name', building.name)
  entity.setField('postalAddress', building.postalAddress)

  entity.setSelected(true)

  return entity
}

Put it together

Replace the example Ref IDs, building names, and addresses with values for your buildings.

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const BUILDINGS = {
  'building-ref-id-1': {
    name: 'Example Campus Building A',
    postalAddress: '100 Main Street, Springfield, MA 01103',
  },

  'building-ref-id-2': {
    name: 'Example Campus Building B',
    postalAddress: '200 Main Street, Springfield, MA 01103',
  },

  'building-ref-id-3': {
    name: 'Example Campus Building C',
    postalAddress: '300 Main Street, Springfield, MA 01103',
  },
}

export default function rules(entity: RulesEntity): RulesEntity {
  const building = BUILDINGS[entity.getRefId()]

  // If this building is not in the lookup table,
  // leave it unchanged so it can be reviewed.
  if (!building) {
    return entity
  }

  entity.setField('name', building.name)
  entity.setField('postalAddress', building.postalAddress)

  entity.setSelected(true)

  return entity
}

Why This Pattern Is Recommended

  • Ref IDs are typically more stable than names.
  • Names can be standardized independently of the source system.
  • Postal addresses can be maintained centrally.
  • New buildings remain visible for review instead of receiving guessed values.
  • The same rule automatically applies on every future import.

Rules FAQ

Do I have to use rules? No. They're optional advanced configuration. Connectors can be set up entirely by ticking and editing in the Grid.

Can a rule delete entities? A rule can untick a row (setSelected(false)), which un-selects an already-saved entity on the next Save. Items that are un-selected and then saved are intended to be removed from the Mapped Graph, but there is currently no capability to delete entities from the connector runtime. There's work for that to be a possibility in the future. In the meantime, the entity may become an orphan in the Graph.

What if the rule has an error? The editor shows it in the status bar and underlines it in the code. It can be saved so that you save your work.

What kind of errors can happen with rules? There are two different kinds of "error", and only one shows up in the Rules editor. First, the editor may complain (red underline, orange status bar), which indicates the rule doesn't quite typecheck. It still saves and still runs.

Second, the rule may crash while running, and this is indicated in the Grid, not the editor. In this case the rule does not apply. On every affected row, a red icon replaces the checkbox, and the row can't be selected or deselected until the rule is fixed. Hovering it says whether the rule failed on that one row or on every row.

Where do I see what fields I can use? The read-only types.d.ts tab in the Rules editor — it's generated per connector and always current.

Entities Migration

Some connectors were originally configured with the older place-mappings model and are being moved onto Entities. That move is called Entities Migration. Migration is a one-time rename that switches a connector to the new Entities screens without rebuilding anything or losing any data, connections, or history.

Mapped is still rolling out the Connector Entities framework across connectors, so migration currently has limited availability.

When is migration available?

If a connector has been configured before Entities existed, its data currently lives in the old place-mappings layout (a Site → Building → Floor → Space tree). To use the new Entities Grid and Merge Rules, that connector needs a one-time migration. Until it's done, the connector shows:

  • The old mapping table (still fully working — you can keep using it), and
  • A red "Entities Migration" item at the bottom of the section list (just above Logs).

The new Entities screens stay hidden until the migration is complete. A connector already on Entities won't show any of this.

What does migration do?

Migration DOES:

  • Renames each existing item to the new Entities naming scheme.
  • Marks the migrated items as saved/merged so they appear, fully filled in, in the new Entities Grid.
  • Flips the connector to "migrated," after which only the Entities screens remain.
  • Clears the connector's old place mapping configuration and any Rules-Based Place Mapping.
  • Temporarily stop the connector for a few minutes.

Migration DOES NOT:

  • Rebuild the graph. Your existing buildings, floors, spaces, and devices stay exactly where they are.
  • Re-import from the source system. Migration works entirely off what's already there.
  • Lose history or telemetry. Every existing connection (which room is on which floor) and all timeseries data stay attached.
  • Delete anything. Items the new model doesn't directly own become "orphans" — recorded for review, but never touched, detached, or deleted.

How do I migrate?

Migration is done from the red Entities Migration section — it's a normal section, not a pop-up.

1 . Open the Entities Migration section. The old mapping table is still there and still works.

2 . Click Generate Report (top-right, where Import/Save normally sit). This previews the migration without changing anything. The report shows one tab per type:

  • Supported types first — the items that will migrate cleanly. Each tab lists them in a read-only table (columns include the old key, the new key, and each field value).
  • Orphan types last, shown in red — items the new model doesn't own. These are listed for awareness; they're left untouched.
  • Each table has an Export CSV button if the customer wants a copy to review offline.

3 . Review the report. This is the moment to check that the supported items look right and understand what's flagged as an orphan.

  1. Click Migrate (top-right). This performs the rename, marks the items as saved, and switches the connector to Entities.

  2. Afterwards: the old mapping table disappears, and the Entities screens appear — already populated with the migrated items, no re-import needed. If the connector remains in a STOPPED state, press Start to re-start polling. Run an import when you need fresh data from the source.

What happens after migration?

  • The connector now behaves like any Entities connector: Import, edit the Grid, write Merge Rules, Save. Review the Entities walkthrough and Merge Rules.
  • The Entities Migration section stays visible as a read-only record. Instead of Migrate, it shows a disabled "Migrated on <date>" status and a Download Pre-Migration Config button — a one-time export of exactly how the connector was configured before migration, kept for reference.
  • Migration is once per connector. It doesn't run again.
  • For each entity, the Grid is filled in with what was configured previously. The Rules tab will be empty.

What should I know about migration?

  • Nothing is deleted or rebuilt — it's a rename of existing items.

  • Generate Report changes nothing — they can preview as many times as they like before committing.

  • All connections and history are preserved. During migration, the connector stops briefly, during which period new sensor readings may be missed. Treat it like a short maintenance window. Migration should only take a few minutes, but the connector needs to be manually re-started afterwards by pressing the Start button.

  • Orphans are safe — they're recorded, not removed; a person can decide what to do with them later.

  • It's one-time — once migrated, they simply use the Entities screens from then on.