Docs/Timebase Components/Instance Maps
AtlasHow-to

Instance Maps

An Instance Map is the recipe that turns a type in your Ontology into the real things you want to see in Atlas. The Ontology says what a Pump is.

Updated 6 Nov 2026Timebase Docs

Overview#

An Instance Map is the recipe that turns a type in your Ontology into the real things you want to see in Atlas. The Ontology says what a Pump is. The Instance Map says which pumps exist, what each one is called, and where each one's values come from.

You would read this when you have finished defining types and you now need actual equipment in the domain: one site-level asset, a fixed set of twelve fillers, or one instance for every row in an asset register a connector can read.

Two terms used throughout, in plain English:

  • Connector - a component that talks to an outside system (the Timebase Historian, an OPC UA server, a SQL database, an MQTT broker) and feeds data into Atlas.
  • Compile - the step that turns your saved definitions into the live model. Nothing you author here exists as an instance until you compile.
Note

This article covers building instances. It does not cover where each property's value comes from. For Static, Bound and Calculated sourcing strategies and the exact format of a connector binding path, see the article Property Sourcing Strategies - Static, Bound, and Calculated.

The three shapes an instance map can take#

An instance map definition produces one instance, a fixed set of instances, or one instance per row a connector returns. The names Atlas uses internally, and which you will see in error text and support conversations, are:

Shape What it produces What makes it that shape
Singleton Exactly one instance No Source, and no variable that produces more than one value
StaticTemplate One instance per value in the longest variable At least one variable whose data produces more than one value
RowsetTemplate One instance per row the connector returns A Source is present
Note

There is no field where you pick the shape, and there is no picker in the drawer. Atlas works it out from what you filled in. If you add a Source, it is a rowset template. If you have no source but a variable holding 1-5, it is a static template. Otherwise it is a singleton.

Note

There is no shape called "Bound Template". If a document or a colleague uses that name, they mean RowsetTemplate.

Ask Gage

Ask the instance-builder agent: "Create a Singleton instance of PumpStation. Use instance id pump-station-north, instance label North Pump Station. Set all properties to Static for now (no connector yet)."

Requires the pulse:administrator role. Without it the write tools are filtered out before the agent runs, so it will simply say it cannot do this rather than returning a permission error.

Create instances in the Instance Builder#

You do all of this in the browser. You never have to open a file.

  1. In the sidebar, select Domains.

  2. In the Branch select beside the filter box, choose the branch you want to work on.

  3. Find your domain in the list and select Pin on its row. The domain now appears under Domains in the sidebar with its own set of views: Overview, Types, Instance Builder, Instances and Canvas.

  4. Select Instance Builder. Definitions you have already created are listed here, grouped by type under an Entities section and a Relationships section.

    The Instance Builder: definitions grouped by type under Entities and Relationships, the Fillers definition selected, and the inspector summarising its variables and mappingsThe Instance Builder: definitions grouped by type under Entities and Relationships, the Fillers definition selected, and the inspector summarising its variables and mappings

  5. Select Create (the button carries a small ▾) and pick one of the four entries:

    • Single Instance - one named thing, for example a site or a control system.
    • Entity Instance Map (Bulk) - many things from one definition.
    • Single Relationship - one connection between two named instances.
    • Relationship Instance Map (Bulk) - many connections from one definition.
  6. Fill in the drawer that opens (the fields are described below), then select Create. The button reads Save when you reopen a definition to edit it. The cross at the top right closes the drawer without saving.

A successful save leaves the drawer open, so you can keep working on the same definition. 7. Open the domain's Overview view and select Compile. The button reads Recompile once the domain has been compiled at least once. 8. Open the Instances view to see what you built.

Note

Steps 7 and 8 are not optional. Saving a definition writes it to the branch, but it does not create anything. Until you compile, the Instances view will not show your new instances.

The bulk drawer#

The bulk entries open a drawer titled New instance map (or Edit instance map when you reopen one). It has a File selector at the top and three tabs: General, Variables and Mappings. The Variables tab shows a live count in its own label, for example Variables (3).

The single-instance drawer#

The single entries open a drawer titled New instance (or Edit instance). It has the same General fields but only two tabs, General and Properties. It has no Variables tab and no Source block, because a single instance has nothing to repeat over.

The General tab, field by field#

Field Required What to put in it
Definition ID Yes A short identifier for the definition itself, for example machines. It must be unique across every instance map file in the domain, not just the file you are saving into. Locked after creation
Definition name Yes A readable name for the definition, for example Test Machines. This is metadata about the recipe. It is not what the Instances view shows for each instance
Type Yes The Ontology type each instance will be. For a relationship map, the picker offers only relationship types
Instance ID Yes The identity of each instance produced. Use placeholders to make it different per instance, for example Machine-[Num, 00]
Instance label No The display name shown in the Instances view, the instance table and the inspector. Same placeholder syntax as Instance ID, for example Machine [Num, 00]. Leave it blank and instances show their raw identifier instead
Description No Free text for whoever reads the definition next
Source No The optional connector block that drives a rowset template. See below
Note

Definition name names the recipe. Instance label names each thing the recipe produces.

For a single instance, put the exact identity in Instance ID with no placeholders, for example pump-station-north.

The General tab of the Fillers definition: Definition ID locked, Definition name, Type Filler, the optional Source block, Instance ID with a Num placeholder, and Instance labelThe General tab of the Fillers definition: Definition ID locked, Definition name, Type Filler, the optional Source block, Instance ID with a Num placeholder, and Instance label

Variables decide how many instances you get#

Open the Variables tab and select + Add variable. Each variable has a Name, a data value and a wrap switch. The drawer states the rule on the tab itself:

Named tokens, referenced as [Name] (e.g. in the Instance Id). Literal data - a range like 1-5 or a comma list - drives how many instances are created.

The Variables tab: one variable named Num with the data 1-10, its wrap switch, and the Add variable buttonThe Variables tab: one variable named Num with the data 1-10, its wrap switch, and the Add variable button

Placeholder syntax#

A placeholder is the variable's name in square brackets: [Num]. Add a format after a comma to pad numbers: [Num, 00] gives 01, 02, 03. A letter prefix works too: [Num, FL00] gives FL01.

Important

The placeholder syntax is [Name]. It is not {i} and it is not $-prefixed. Atlas does not recognise {i}, so it is left in the text exactly as typed. A definition whose Instance ID reads pump-north-{i} has no variables at all, which makes it a singleton, so you get exactly one instance literally called pump-north-{i}. Nothing warns you.

Data formats#

What you type What you get
North One value: North
a, b, c Three values: a, b, c
1-5 Five values: 1, 2, 3, 4, 5
01-05 Five values with the padding kept: 01, 02, 03, 04, 05
0-8/2 Five values: 0, 2, 4, 6, 8
High*3, Low*1 Four values: High, High, High, Low
Important

in the Value*count form the value takes letters and digits only, with no space before the *. Row 1*8 is read as a plain comma-separated list rather than a multiplier, so write Row1*8.

How many instances you actually get#

This is the single most misunderstood part of instance maps.

Important

Atlas expands variables linearly, not as a cross product. The variable producing the most values sets the instance count. Every other variable is read at the same position. Two variables of three values each give you three instances, not nine.

When a shorter variable runs out of values, its wrap setting decides what happens next. With wrap off (the default) it repeats its last value. With wrap on it starts again from the beginning.

Worked example - what you get. Cooling towers, two variables:

Variable Data Values
Area North, South North, South
Num 1-3 1, 2, 3

Instance ID is CT-[Area]-[Num, 00]. The longest variable is Num with three values, so you get three instances:

Position Area Num Instance ID
1 North 1 CT-North-01
2 South 2 CT-South-02
3 South (repeated, wrap off) 3 CT-South-03

Three instances, and two of them are in the South. This is almost never what someone wants when they write it.

Worked example - what you probably meant. To get six cooling towers, three per area, make both variables six values long:

Variable Data Values
Area North*3, South*3 North, North, North, South, South, South
Num 1, 2, 3, 1, 2, 3 1, 2, 3, 1, 2, 3

The same Instance ID pattern now gives CT-North-01, CT-North-02, CT-North-03, CT-South-01, CT-South-02, CT-South-03. The alternative, which is often clearer to read later, is one definition per area.

Ask Gage

Ask the instance-builder agent: "Create a StaticTemplate that builds 6 cooling towers: CT-North-01, CT-North-02, CT-North-03, CT-South-01, CT-South-02, CT-South-03. Use variables Area (North3, South3) and Num (1, 2, 3, 1, 2, 3). Instance id is CT-[Area]-[Num, 00]."

Requires the pulse:administrator role. Without it the write tools are filtered out before the agent runs, so it will simply say it cannot do this rather than returning a permission error.

Every variable must be used#

If you add a variable and never reference it, saving fails. The message names the variable:

map[] entry name 'Area' is an orphan — no instanceId, label, property strategy, or relationship template references it.

Referencing it from the Instance ID, the Instance label, a property value, a binding path or a relationship endpoint all count.

Build instances from a connector with a Source#

A Source turns one definition into one instance per row a connector returns. This is how you build a domain from an asset register or a tag catalog instead of typing every asset in by hand.

On the General tab, select + Add source and fill in:

  • Source name - your own name for it, for example fillers. You will use this name from your variables.
  • Source binding path - the connector path whose rows drive the count. The field hint is connector/path.

Then, on the Variables tab, set a variable's data to a column reference: [fillers.FillerNumber]. Atlas fetches the rowset once at compile time and creates one instance per row.

The Variables tab of a definition driven by a Source: five variables, each reading a column of the catalog source such as catalog.Num and catalog.descriptionThe Variables tab of a definition driven by a Source: five variables, each reading a column of the catalog source such as catalog.Num and catalog.description

Use Remove source to go back to a plain variable-driven definition.

Transforms#

A raw source column is often a tag name with the useful part buried inside it. Select + Add transform to carve new columns out of one, using a regular expression with named capture groups. The two inputs are hinted raw column and (?<Capture>\d+). Each capture becomes a new column you can reference as [sourceName.CaptureName].

The General tab with a Source named catalog on the path historian-juice-factory/The Juice Factory, and two transforms: one carving Num out of the tag name, one carving Unit out of the uom columnThe General tab with a Source named catalog on the path historian-juice-factory/The Juice Factory, and two transforms: one carving Num out of the tag name, one carving Unit out of the uom column

Important

A row whose transform regex does not match is dropped, not passed through. If a transform is slightly too strict you get fewer instances than the source has rows, with nothing on screen to say so. When a definition has several transforms, a row must match all of them to survive. To read a source's description, unit or format into properties, including values held in a JSON column, see Using connector metadata in instance maps.

If the connector returns no rows at all, or every row is dropped by a transform, the definition produces zero instances. That is recorded as a warning in the Atlas log, not as an error, so the compile still reports success.

A worked shape, taken from a shipped test fixture:

{
  "id": "bound-machines",
  "name": "Bound Test Machines",
  "type": "urn:atlas:ontology:juice-factory:Machine",
  "instanceId": "BoundMachine-[Num, 00]",
  "sources": {
    "fillers": {
      "bindingPath": "historian-juice-factory/The Juice Factory/FillerTemps",
      "transforms": {
        "name": { "regex": "(?<Area>\\d+)\\-(?<Instrument>\\w+)\\-(?<FillerNumber>\\d+)\\.PV" }
      }
    }
  },
  "map": [
    { "name": "Num", "data": "[fillers.FillerNumber]" },
    { "name": "Tag", "data": "[fillers.name]" }
  ]
}
Note

A definition can declare several sources but its variables may only reference one of them. Referencing two is rejected.

One useful difference: the Source path is checked, the property path is not#

This is the one asymmetry worth remembering.

  • A Source binding path is checked when you save. If it does not resolve through a registered connector, saving reports rule I-B10 and the dependent checks on that source are skipped.
  • A Bound property's binding path on the Mappings tab is matched against the connector's advertised paths at start-up rather than when you save, so copy the path rather than typing it.

So if a rowset produces no instances you will usually get told. If a property never shows a value, nothing tells you at all. The property side is covered in Property Sourcing Strategies.

Ask Gage

Ask the instance-builder agent: "Create a RowsetTemplate that builds one Pump instance per row from a connector. Source path is historian-plant/equipment-register. Add a transform that extracts PumpNumber from tag names like 131-PUMP-01.PV using the regex (?\d+). Instance id is Pump-[PumpNumber, 00]."

Requires the pulse:administrator role. Without it the write tools are filtered out before the agent runs, so it will simply say it cannot do this rather than returning a permission error.

Relationships are instance maps in their own right#

A relationship in Atlas is not a sub-list inside an entity definition. It is its own instance map, with its own type and its own properties.

  1. In Instance Builder, select Create ▾ and pick Single Relationship for one connection, or Relationship Instance Map (Bulk) to create many at once.
  2. The same drawer opens, with the Type picker restricted to relationship types.
  3. Once you choose a type, two extra fields appear: Source and Target. Both are marked required.
  4. Each field takes the instance identity of one end, not the definition ID that produced it. In the bulk drawer both accept [Name] placeholders and are resolved once per row, exactly like Instance ID.

Each end's picker is scoped to the type that end of the relationship expects, so it only offers instances that can legally sit there.

The General tab of a relationship definition: Type Production Line Contains, Source set to the Line 1 instance and Target set to the Fillers definition, each field naming the type it expectsThe General tab of a relationship definition: Type Production Line Contains, Source set to the Line 1 instance and Target set to the Fillers definition, each field naming the type it expects

{
  "id": "feeds-pump01-tank01",
  "name": "Feeds-Pump01-Tank01",
  "type": "urn:atlas:ontology:juice-factory:Feeds",
  "instanceId": "urn:atlas:instance:juice-factory:feeds-pump01-tank01",
  "source": "urn:atlas:instance:juice-factory:pump-01",
  "target": "urn:atlas:instance:juice-factory:tank-01",
  "properties": {
    "urn:atlas:ontology:juice-factory:flowRate": [
      { "type": "StaticStrategy", "value": 2.5 }
    ]
  }
}

Because the two ends are looked up across every instance in the domain, they can come from completely different definitions and different files. That is the point: you can build the equipment from one asset register and the connections from another.

A relationship carries properties the same way an entity does, in its Mappings tab. There is no separate place for edge properties.

Important

author each connection as its own instance map definition. A relationships[] array on an entity definition is not part of the shape Atlas reads, and because an unrecognised property is ignored, an API call that sends one returns 201 Created and creates no edges. You get a green light and an empty graph. If you have scripts or old files using relationships[], rewrite them as separate relationship definitions.

Two errors you will meet if an endpoint is wrong:

A relationship instance map must declare both 'source' and 'target'; 'target' is missing.

Relationship instance 'feeds-pump01-tank01' references target 'urn:atlas:instance:juice-factory:tank-1', which does not match any expanded instance id in the domain.

The second one almost always means you typed a definition ID where an instance identity belongs, or the instance ID pattern padded differently than you expected (tank-01 against tank-1).

Set property values on the Mappings tab#

Open the Mappings tab (called Properties in the single-instance drawer). Every property the chosen Type declares is listed, each with a chip showing where its value currently comes from. Select a property and a segmented control offers Static, Bound, Calculated and Reference. A property whose value type is a data type also offers Composed, which sources each field of the value separately.

The Mappings tab: a count of strategies in use, then one row per property with its value and a chip reading Static, Bound or Calculated, one of them marked Type defaultThe Mappings tab: a count of strategies in use, then one row per property with its value and a chip reading Static, Bound or Calculated, one of them marked Type default

A row carrying a Type default badge is not mapped here: its value comes from a default set on the property type, and editing the row authors an instance-level mapping that overrides it. See Set a default on the property type.

For what each strategy means, when to use it, and the exact format of a connector binding path, see Property Sourcing Strategies - Static, Bound, and Calculated. That article is the reference for all of it.

Two rules that belong here because they are about the instance map file itself:

  • Property keys must be the full URI. In a saved file, a property is keyed by its complete urn:atlas:ontology: identifier. A short name such as "Site" is rejected on save with:

    Property key 'Site' is not a full URI. Property keys must start with 'urn:atlas:ontology:'.

  • The property must belong to the type. Attaching a property the type does not declare (and does not inherit) is rejected with a message that starts:

    Property 'urn:atlas:ontology:my-domain:Speed' is not attached to type 'urn:atlas:ontology:my-domain:Pump'

Placeholders work inside property values and binding paths too, so one definition can give each instance its own tag: historian-juice-factory/The Juice Factory/131-TT-[Num].PV.

Where instance maps are stored#

Definitions live in files ending .map.json, in the domain's instances/ folder in Git. One file holds many definitions. You pick the file in the File selector at the top of the drawer when you create a definition, and + Create new file… lets you start a new one (Atlas adds the .map.json ending for you if you leave it off).

The New instance map drawer: the File selector at the top, then the General, Variables and Mappings tabs with the General fields emptyThe New instance map drawer: the File selector at the top, then the General, Variables and Mappings tabs with the General fields empty

How you spread definitions across files is up to you. Common groupings are one file per physical area, or one file per asset type. There is one rule that does bite:

Important

A Definition ID must be unique across every file in the domain, not just within its own file. A duplicate is rejected with: Instance id 'machines' is not globally unique across the domain; it appears in 'instances/north.map.json', 'instances/south.map.json'. Every instance map entry must have a unique id across all *.map.json files.

If you hand-edit a file#

You do not need to, and for day-to-day work you should not. If you do:

Important

Atlas rejects any property name it does not recognise, and it rejects the whole file, not just the offending definition. One stale field name from an older article - instanceBinding, Bindings, BindKey, relationships - takes out every definition in that file at once. The file also has to have a JSON array at its root.

You will see one of these:

Instance map file root must be a JSON array; got Object.

Instance map file is not parseable JSON at line 14, column 7: ...

Note the difference in behaviour: over the API an unknown property is ignored, while in a file it stops the whole file loading.

Compile, and what happens to instances that disappear#

Select Compile on the domain's Overview view. Every compile is a full rebuild: Atlas expands every definition from scratch and replaces the previous model entirely.

Important

Nothing is preserved across a compile. If a row leaves your source register, the instance it produced is simply not built the next time you compile, and it disappears from the Instances view and from queries. Atlas does not keep an instance alive because it existed before. Values already written into the Timebase Historian are untouched, but the instance is no longer in the Atlas model.

Atlas recompiles every domain it knows about when the service restarts, so you do not need to compile again after a restart. The exception is a domain that depends on a connector: it waits until that connector connects and advertises its paths, and if the connector never appears the domain stays uncompiled.

Outstanding items and Unpublished changes#

Both sections sit on the domain's Overview.

  • Outstanding items lists validation problems with a chip reading "{n} to fix". Your changes are saved, and these do not block saving, but the domain cannot be compiled until they are resolved. Each line reads [RuleId] message, for example [I-A9] A relationship instance map must declare both 'source' and 'target'; 'target' is missing.
  • Unpublished changes only appears when a save of yours failed validation. When it does, a chip reads Ready to publish or "{n} to fix first", and a button reads Publish to {branch}.
Important

Once Unpublished changes appears, no further save reaches the branch, even a perfectly valid one. Everything keeps returning success and nothing changes. If you saved, compiled, and nothing happened, look here first, fix the outstanding items, then select Publish to {branch} and compile again.

Edit or delete a definition#

In Instance Builder, select a definition in the tree. The inspector on the right summarises its sources, variables and mappings and offers Edit, which reopens the drawer. Delete sits at the foot of the drawer, and asks you to confirm:

Delete instance definition 'Test Machines'? This permanently removes the definition. This action cannot be undone.

Note

You delete definitions, never individual instances. Instances are the output of a compile. To change or remove an instance you change the definition that produces it and compile again. The Instances view is for browsing and inspecting only.

A delete can be refused if something still points at the definition, for example a relationship whose Source or Target would be left dangling. The refusal lists the violations that blocked it.

Ask the Instance Builder agent#

Atlas ships an assistant that authors instance maps for you. Open Agents in the sidebar and choose instance-builder. Describe what you want in plain language, for example: "Create 8 pumps in the North Pump Station, instance ids Pump-01 to Pump-08, with Speed bound to the historian tags 131-PT-01.PV through 131-PT-08.PV."

The agent writes the definitions into .map.json files for you. Review what it produced in Instance Builder before you compile - it writes to your branch just as you would.

Troubleshooting#

Symptom Likely cause What to do
You saved a definition but the Instances view is empty Saving does not create instances. Nothing exists until the domain is compiled Open the domain's Overview and select Compile (or Recompile). Then reopen Instances
You saved and compiled, both succeeded, and nothing changed Your save failed validation, so it went into your private draft and the branch never advanced. Both requests still returned success On Overview, look for Unpublished changes. Fix the items listed under Outstanding items, select Publish to {branch}, then compile again
You expected six instances and got three Variables expand linearly, not as a cross product. The longest variable sets the count and the others are read at the same position Make every variable the same length. For three per area use North*3, South*3 alongside 1, 2, 3, 1, 2, 3, or split it into one definition per area
You got exactly one instance, and its identity contains the literal text you typed as a placeholder The placeholder was written {i}. Atlas only recognises [Name], so {i} was left in the text and the definition had no variables at all, which makes it a singleton Rewrite the Instance ID using [Num] or [Num, 00], and add a variable named Num with data such as 1-6
Instance identities come out unpadded (Pump-1, not Pump-01) The placeholder has no format specifier Use [Num, 00] for two digits, [Num, 000] for three. Alternatively write the range with leading zeros (01-06), which preserves the padding
A rowset definition produces fewer instances than the source has rows A transform regex did not match some rows, and non-matching rows are dropped without a message Loosen the regex, or test it against a raw column value from the source. Check the Atlas log for the per-transform drop counts
A rowset definition produces no instances at all The connector returned zero rows, or every row was dropped by a transform. Both are logged as warnings, so the compile still reports success Confirm the connector is online in the Connectors panel on the Home page. Check the Source binding path and the transform regexes, then look in the Atlas log for the "0 rows" warning
Saving fails with Instance id 'machines' is not globally unique across the domain Two definitions share a Definition ID, possibly in different files Rename one of them. Definition IDs are unique across the whole domain, not per file
Saving fails with map[] entry name 'Area' is an orphan You added a variable and nothing references it Reference [Area] from the Instance ID, the Instance label, a property value or a relationship endpoint, or delete the variable
Saving fails with instanceId template references '[Num]' but no map[] entry declares name 'Num' The Instance ID or Instance label uses a placeholder that no variable declares Add the variable under that exact name, or correct the spelling. Placeholder names are case-sensitive
Saving fails with Property key 'Site' is not a full URI A property in the file is keyed by its short name Use the complete identifier: urn:atlas:ontology:<YourDomainHere>:<PropertyNameHere>
Compiling fails with Property 'urn:...:Speed' is not attached to type 'urn:...:Pump' The property is not declared on this type or any of its parents Open Types, confirm the property is attached to the type you selected, and check the spelling and capitalisation
Relationships save and the graph has no connections The relationships[] form was used. Unknown properties are ignored over the API, so the write returns 201 Created and creates no edges Rewrite each connection as its own definition with a relationship Type and both Source and Target set, using Create ▾ → Single Relationship or Relationship Instance Map (Bulk)
A relationship reports ... does not match any expanded instance id in the domain Source or Target holds a definition ID, or an identity that does not match what the other definition actually produced Open Instances and copy the exact identity of each end. Watch for padding: tank-01 and tank-1 are different
Saving a relationship fails with A relationship instance map must declare both 'source' and 'target' Only one end was filled in Fill in both. A relationship needs both ends; an entity map must have neither
Hand-editing a .map.json file makes every definition in it disappear Atlas rejects unknown property names and rejects the whole file, not just the bad definition. A stale field such as instanceBinding, Bindings or relationships does this Remove the unrecognised field. Check the error for the reported line and column. In future, author in the drawer instead
An instance you had yesterday is gone after a compile Its row left the source register. Compiling is a full rebuild and nothing is carried over If the asset should still exist, restore the row in the source system, or move that instance into a definition that does not depend on the source
The instance rows in the Instances view show raw identifiers instead of readable names The definition has no Instance label Reopen the definition, fill in Instance label (for example Machine [Num, 00]), save, and compile. Definition name does not affect this
A bound property never shows a value even though instances were created A Bound binding path is not checked when you save, so a wrong path compiles cleanly and binds nothing See Property Sourcing Strategies - Static, Bound, and Calculated. Confirm the connector is online in the Connectors panel on the Home page, then compare the path character for character against a working one
esc
↑↓ navigate · ↵ open · ⌘K anywhere