An action applies edits to the Ontology through a single transaction that can read from the Ontology, apply user-defined logic, and write edits to one or more objects and links.
This page describes the guarantees that action types provide during concurrent execution and how to choose the right isolation level for a particular use case.
Actions behave as database transactions and provide the standard ACID properties:
After an action completes, any action or query that starts afterward sees all edits applied by the completed action. This guarantee does not apply to reads performed during the same action execution.
The visibility of those edits depends on the action's write mode, as described in How writes work.
Whether an action writes to a property depends on how the action is implemented:
When you apply an action through the Ontology SDK (OSDK), you can omit an optional parameter from the action request only if the parameter has a default value configured. If the default value uses the current value of an object property, the action fills in the omitted parameter with that property value and writes it back to the property.
Actions provide two write modes. In both modes, the action's edits are collected while the action runs and committed to the Ontology as a single atomic batch at the end, so other actions never observe a partially applied action. The two modes differ in whether the action's own reads can see the edits it has made so far:
At isolation levels that perform conflict checks, the Ontology detects write-write conflicts at the object level. If two actions concurrently write to the same object, one action fails with a conflict error. This applies even if the actions write to different properties of that object.
A rebuild of a dataset that backs an object type or many-to-many link type can also cause conflicts. Conflicts between action executions are limited to objects that both actions write, but conflicts caused by backing dataset rebuilds are broader. An action may encounter a conflict while a rebuild is running even if the rebuild does not change the specific object that the action writes. These conflicts are transient, so retrying the action after the rebuild completes typically succeeds.
When a conflict is detected, the failing action's entire edit is discarded. See Retry mode on transient errors for how conflicts are surfaced and retried.
An action type's isolation level sets the guarantees that apply when an action runs alongside other actions interacting with the same data. It defines two things: which version of the data each read inside the action observes, and which concurrent changes cause the action to fail rather than commit a result built on data that has since changed. A stronger level rules out more anomalies but rejects more actions on conflict; a weaker level commits more often but offers fewer guarantees.
An action type's isolation level is configured in Ontology Manager, in the action type's Capabilities tab. The level can be changed after creation and takes effect for subsequent executions.
All new action types that use batched writes are created with snapshot isolation by default. Action types that use staged writes currently support only legacy mode. For more information, see Current staged writes behavior.

The levels available for action types are summarized below and described in detail in the sections that follow.
| Level | Reads | Conflict checks |
|---|---|---|
| Legacy | Reads are not guaranteed to come from a single point in time. Different reads in the same action can observe different states. | Fails if another action changed an object this action wrote. |
| Snapshot isolation | All reads use one snapshot, taken when the action starts. | Fails if another action changed an object this action wrote. |
| Read committed | Each read returns the latest committed value at the time of the read. | Edits are applied even if another action changed the same object. |
The legacy isolation mode remains available in the Capabilities tab for action types created before explicit isolation levels were introduced. New action types that use batched writes default to snapshot isolation instead.
Reads: Reads usually come from a single point in time, but this is not guaranteed. Different objects loaded during the same action execution may reflect different states of the Ontology because the underlying data can be refreshed while the action is running due to errors in retrieving the data from object storage.
Writes: Legacy isolation supports both write modes. With batched writes, reads performed later during the same action execution do not include edits made earlier in that execution. With staged writes, later reads include those edits. For more information, see How writes work and Current staged writes behavior.
Conflict checks: An action fails with a conflict error if another action changes an object it writes between the time the object is loaded and the time the action commits. However, because reads are not guaranteed to be consistent, a decision based on data the action only read may be made on a view that combines data from different points in time.
The legacy isolation mode is provided for backward compatibility.
For new action types, use snapshot isolation to get the same conflict checks with the additional guarantee that all reads come from a single point in time.
This is the default level for new action types that use batched writes. For the behavior of action types that use staged writes, see Current staged writes behavior.
Logic rule and function-backed actions read the Ontology from one consistent point in time. When a function calls a source, the source operates outside the action's point-in-time view. A source may read from the Ontology or other systems, and those reads may observe data from a different point in time. The action's atomicity and conflict checks do not cover operations that a source performs.
Reads: Every Ontology read performed directly by the action observes a single consistent point-in-time view, taken when the action begins. Reading the same object or running the same search twice always returns the same matching data, regardless of what other actions commit while this action runs.
Writes: The action uses batched writes, so reads performed later during the same action execution do not include edits made earlier in that execution.
Conflict checks: An action fails with a conflict error if another action changes an object it writes between the time the object is loaded and the time the action commits. The action fails rather than overwriting the concurrent change. Objects the action only reads do not contribute to conflict failures at this level. For information about retrying conflicts, see Retry mode on transient errors.
Because only written objects are conflict-checked, snapshot isolation does not detect read-write conflicts.
Two actions can each read overlapping data, make a decision based on that read, write to different objects, and both commit — even though neither would have committed had it seen the other's write.
This is known as write skew.
Example: An on-call rotation must always keep at least one engineer on call. An engineer may leave the rotation only if someone else remains, so the action checks: are at least two engineers currently on call? Engineers A and B are both on call, and each runs the action to remove themselves at the same time:
A.onCall = false; B writes B.onCall = false. The two actions wrote different objects, so there is no write-write conflict and both commit. The rotation is now empty, violating the invariant.Snapshot isolation cannot catch this because the violated invariant lives on data the actions read (the set of on-call engineers), not on any single object either action wrote.
To avoid this anomaly, redesign the workflow to express the invariant as a write to a single shared object.
For example, both actions could write to a Rotation object that has an onCallCount property, causing the actions to conflict.
The read committed isolation mode will be available soon.
Reads: Each read returns the latest committed value at the moment the read runs. There is no fixed snapshot, so two reads taken at different points in the same action can observe different states if another action commits between reads.
Writes: The action uses batched writes, so reads performed later during the same action execution do not include edits made earlier in that execution (see How writes work).
Conflict checks: A concurrent modification to an object this action also writes does not cause a failure — the last action to commit wins, and the earlier action's edit is discarded.
Example: A Ticket object has status = "open" and priority = "low". Two actions run at the same time:
status = "in progress".priority = "high".status = "in progress", priority = "low".status = "open" and priority = "high". Action A's change to status is lost.Under snapshot isolation, Action B would instead fail its conflict check because both actions wrote the same object, and Action A's edit would be preserved.
This level produces higher commit success rates at the cost of weaker correctness guarantees. It is appropriate when an action only writes values it already has in hand — such as values passed in as parameters — and does not rely on what it reads to decide what to write.
Use read committed with caution when multiple actions can edit the same object concurrently. One action may commit while another is still running. When the later action commits, it can overwrite the earlier action's changes without reporting a conflict.
Action types that use staged writes currently support only legacy isolation mode.
This section describes the guarantees that staged writes provide.
Reads: Each read returns the latest committed value at the moment the read runs, plus the action's own staged edits. There is no fixed snapshot, so two reads taken at different points in the same action can observe different states if another action commits between reads. However, the action's own staged edits are always visible to its later reads.
Writes: The action uses staged writes, so reads performed later during the same action execution include edits made earlier in that execution. Other actions do not see the edits until they are committed atomically at the end (see How writes work).
Conflict checks: An action fails with a conflict error if another action changes an object it writes between the time the object is loaded and the time the action commits. The action fails rather than overwriting the concurrent change. Objects the action only reads do not contribute to conflict failures. For information about retrying conflicts, see Retry mode on transient errors.
This behavior combines the read-your-own-writes capability of staged writes with write-write conflict detection, but does not guarantee that reads from the Ontology come from a single point in time.
The platform can automatically retry an action when it encounters a transient error, including conflict errors and failures loading objects from object storage. Each retry re-executes the action from scratch using the action's configured isolation level, up to 5 attempts. Logic that reads current values recomputes its edits using the data available to the new execution. If every attempt fails, the error is returned to the caller.
By default, actions without external system calls (such as webhooks) are retried, while actions with external system calls are not — because re-executing the action would repeat its side effects, for example firing a webhook twice.
You can adjust this behavior per action type in the Capabilities tab using the Retry mode on transient errors setting:

Note that two otherwise identical action types can behave differently under contention depending on this setting — one appears to always succeed because conflicts are retried away, while the other surfaces conflict errors to its callers.
Automatic retries also mean that an action that conflicts often can take noticeably longer to complete, even when it ultimately succeeds.