> For the complete documentation index, see [llms.txt](https://navixy.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://navixy.com/docs/navixy-graphql-api/optimistic-locking.md).

# Optimistic locking

Preventing lost updates with version-based concurrency control

The API uses the optional `version` field for optimistic concurrency control, preventing lost updates when multiple clients simultaneously edit the same entity.

## How optimistic locking works

Every versioned entity includes a `version` field:

```graphql
type Device {
  id: ID!
  version: Int!
  title: String!
  # ...
}
```

The version starts at 1 when an entity is created and increments with each successful update.

Update and delete mutations accept the current version:

```graphql
input DeviceUpdateInput {
  id: ID!
  version: Int   # Optional. Include to enable conflict detection.
  title: String
  # ...
}

input DeviceDeleteInput {
  id: ID!
  version: Int   # Optional. Include to enable conflict detection.
}
```

## Supported entities

Optimistic locking applies to:

<table data-search="false"><thead><tr><th width="180">Entity</th><th>Description</th></tr></thead><tbody><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/devices.md#device">Device</a></td><td>GPS trackers, sensors, beacons</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/assets.md#asset">Asset</a></td><td>Vehicles, equipment, employees</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/assets/groups.md#assetgroup">AssetGroup</a></td><td>Asset collections</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/geo-objects.md#geoobject">Geo object</a></td><td>Geofences, POIs, routes</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/schedules.md#schedule">Schedule</a></td><td>Work hours, maintenance windows</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/devices/inventory.md#inventory">Inventory</a></td><td>Warehouse records</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/workspaces.md#workspace">Workspace</a></td><td>Tenants. Read-only in this API, so the version is informational</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/actors/users.md#user">User</a></td><td>User accounts</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/workspaces/members.md#member">Member</a></td><td>Workspace memberships</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/actors/integrations.md#integration">Integration</a></td><td>External system integrations</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/catalogs/catalog-items.md#catalogitem">CatalogItem</a></td><td>All catalog items (device types, asset types, tags, etc.)</td></tr></tbody></table>

## Operations by type

<table><thead><tr><th width="207.5556640625">Operation</th><th>Behavior</th></tr></thead><tbody><tr><td>Create</td><td>Version not applicable. Returns <code>version: 1</code></td></tr><tr><td>Update with <code>version</code></td><td>Returns <a href="/docs/navixy-graphql-api/error-handling.md#version-conflict-409">409 Conflict</a> if the entity was modified since your last fetch</td></tr><tr><td>Update without <code>version</code></td><td>The API reads the current version and updates against it. A change that lands between that read and the update still returns <code>CONFLICT</code>, but a change that landed before your request is overwritten without warning.</td></tr><tr><td>Delete with <code>version</code></td><td>Returns <a href="/docs/navixy-graphql-api/error-handling.md#version-conflict-409">409 Conflict</a> if the entity was modified since your last fetch</td></tr><tr><td>Delete without <code>version</code></td><td>Deletes unconditionally, regardless of any concurrent changes</td></tr></tbody></table>

{% hint style="warning" %}
Omitting `version` on a delete-type operation is particularly risky, as the operation proceeds unconditionally regardless of what happened to the entity since you last fetched it. Always include `version` when deleting unless you have a specific reason not to.
{% endhint %}

## Workflow

Here's the typical flow for updating an entity:

1. Query returns version:

```graphql
query {
  bdr {
    device(id: "550e8400-e29b-41d4-a716-446655440001") {
      id
      version    # Returns 5
      title
    }
  }
}
```

2. Mutation includes the version in its input:

```graphql
mutation {
  bdr {
    deviceUpdate(input: {
      id: "550e8400-e29b-41d4-a716-446655440001"
      version: 5          # Must match the current version
      title: "New name"
    }) {
      device {
        id
        version          # Returns 6 (incremented)
        title
      }
    }
  }
}
```

## Handling conflicts

If you provided `version` and the entity was modified since you fetched it, the API returns a [409 Conflict](/docs/navixy-graphql-api/error-handling.md#version-conflict-409) error. The HTTP status code will be 200 because the request was successfully received and processed. This is a business logic issue, not a transport failure. This follows the [GraphQL-over-HTTP specification](https://graphql.github.io/graphql-over-http/draft/), which reserves HTTP error codes (4xx, 5xx) for transport-level problems like authentication failures or malformed requests.

The actual error details are in the response body:

```json
{
  "errors": [{
    "message": "Version conflict: expected 5, found 6",
    "path": ["bdr", "deviceUpdate"],
    "extensions": {
      "type": "https://api.navixy.com/errors/conflict",
      "title": "Concurrent Modification Conflict",
      "status": 409,
      "detail": "Version conflict: expected 5, found 6",
      "code": "CONFLICT",
      "field": "input.version",
      "expectedVersion": 5,
      "currentVersion": 6,
      "traceId": "2cf7651916cd43dd8448eb211c80319e"
    }
  }],
  "data": { "bdr": { "deviceUpdate": null } }
}
```

Use `extensions.code` for programmatic error handling, not HTTP status codes. See [Error handling](/docs/navixy-graphql-api/error-handling.md) for more details.

When you receive a conflict error:

1. Fetch the entity again to see what changed
2. Merge the other user's changes with yours if needed
3. Retry your update with the new version

## Concurrent editing example

Here's what happens when two users edit the same device:

<figure><img src="https://2533405873-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeaPUYn5ATRhY1fALtOkq%2Fuploads%2Fgit-blob-49cd5411731579e50ad5780711a37b9c955d4c50%2Fconcurrent_editing_diagram_concetpt.png?alt=media&amp;token=1c2fbcd8-2185-48db-abc2-e12f852e76fc" alt=""><figcaption></figcaption></figure>

User A's update succeeds first. User B's update fails because the version changed. After refetching, User B can successfully update with the current version.

## Idempotent commands

Mutations that manage relationships and assignments never require or check the `version` field, because the relationship row has none. Two of them are also idempotent commands: repeating the same call doesn't change the result.

<table data-search="false"><thead><tr><th>Mutation</th><th>Purpose</th></tr></thead><tbody><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/assets/groups.md#assetgroupitemsadd">assetGroupItemsAdd</a></td><td>Add asset to group</td></tr><tr><td><a href="/docs/navixy-graphql-api/products/business-data-repository/api-reference/assets/groups.md#assetgroupitemsremove">assetGroupItemsRemove</a></td><td>Remove asset from group</td></tr></tbody></table>

These operations behave as follows:

* Repeating `add` with the same input returns success without making changes
* Calling `remove` when the relationship doesn't exist returns success without making changes

This design simplifies client code. You can safely retry these operations without worrying about conflicts or checking the current state first.

{% hint style="warning" %}
The device link, identifier, and relation mutations are **not** idempotent:

* `deviceInventoryLink` fails when the device is already assigned anywhere, and `deviceInventoryUnlink` fails when it has no active assignment. See [Managing device inventory](/docs/navixy-graphql-api/products/business-data-repository/guides/managing-device-inventory.md).
* `deviceIdentifierAdd` returns a [409 DUPLICATE](/docs/navixy-graphql-api/error-handling.md#duplicate-409) error when the identifier already exists, on this or any other device, and `deviceIdentifierRemove` returns a [404 error](/docs/navixy-graphql-api/error-handling.md#entity-not-found-404) for an unknown or already-removed identifier ID.
* `deviceRelationCreate` fails when the relation already exists, and `deviceRelationRemove` fails for an unknown or already-removed relation.
  {% endhint %}

## Best practices

1. Always include `version` in your queries. When fetching entities you plan to modify, request the `version` field so you have it ready for mutations.
2. Always include `version` in updates and deletes. The field is optional, but omitting it removes your protection against overwriting changes made by other users since your last fetch. Omit it only for bulk operations where overwriting concurrent changes is acceptable.
3. Be especially careful when deleting without `version`. A wrong update can usually be corrected with another update, but a delete without `version` can remove an entity that another user just changed, and a delete can't be reversed. See [Deleting records](/docs/navixy-graphql-api/deletion.md).
4. Handle conflicts gracefully. In collaborative applications, version conflicts are expected. Implement retry logic or prompt users to review changes.
5. Don't cache versions long-term. Versions can change at any time. Always use the version from your most recent fetch of the entity.

## See also

* [Error handling](/docs/navixy-graphql-api/error-handling.md): Understand error structure, codes, and common error scenarios
* [Managing device inventory](/docs/navixy-graphql-api/products/business-data-repository/guides/managing-device-inventory.md): Assign devices to inventories and track assignment history


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://navixy.com/docs/navixy-graphql-api/optimistic-locking.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
