> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lmrp.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Optimistic concurrency

> The version field on incidents and units, what a 409 means, and how a client should respond.

Several dispatchers and crews change the same incidents and units at once. Argus does not lock records while someone has a form open. Instead, each incident and each unit has a `version` number, and a change must say which version it was based on.

## The rule

1. Every incident and unit you read includes `version`.
2. A change sends that `version` back.
3. If the record has moved on, the change is refused with `409 CAD_CONFLICT` and nothing is altered.
4. A successful change returns the record with its new `version`. Use that for the next change.

```json theme={null}
{ "unitId": "cmg5z2unt000al508unt00001", "version": 3, "status": "EN_ROUTE" }
```

If another request changed the unit first, its version is no longer 3:

```json theme={null}
{
  "error": { "code": "CAD_CONFLICT", "message": "Unit changed. Refresh before saving." },
  "requestId": "3f1c9d5e-7a42-4f0b-9c1e-8b6d2a4e5f70"
}
```

## Which version to send

| Request | Send |
| - | - |
| `POST /cad/incidents/{id}/edit`, `/close`, `/reopen` | The **incident's** version |
| `POST /cad/incidents/{id}/assign`, `/remove` | The **unit's** version, with `unitId` |
| `POST /cad/units/{id}/edit`, `/status` | The unit's version |
| `POST /cad/me/unit/join`, `/status`, `/book-off` | The unit's version, with `unitId` |

Creating a record, attaching, converting or resolving a call, and booking on take no version.

## What advances a version

More than the obvious edits. Plan for the version to change underneath you.

| Record | Advances when |
| - | - |
| Incident | It is edited, closed or reopened; a unit is assigned to it or released from it; a call is attached to it; a unit on it goes off duty |
| Unit | Its details or crew are edited; its status changes; it is assigned or released; someone joins or leaves; its incident is closed |

<Warning>
  Assigning a unit or attaching a call advances the **incident's** version. An edit form that has been open on a busy incident will often be stale by the time it is saved. Re-read the incident just before editing, and keep what the person typed if the save is refused.
</Warning>

## Two kinds of 409

| Code | Cause | Message |
| - | - | - |
| `CAD_CONFLICT` | A rule Argus checked: a stale `version`, a closed incident, a unit that is not available, a status that does not fit the unit's assignment, a full crew | Says which rule |
| `CONFLICT` | The database refused the write: two requests touched the same data at the same instant, or a unique value such as a callsign is already taken | Always "The operation conflicts with the current state." |

CAD changes run in serializable transactions. That is what stops two dispatchers assigning the same unit to different incidents, or a call being converted twice. The price is that one of two simultaneous requests can lose with `CONFLICT` even though neither sent a stale version.

Treat both codes the same way.

## What a client should do

<Steps>
  <Step title="Do not retry automatically">
    The state has changed. The action the person chose may no longer make sense: the unit may already be assigned, or the incident closed.
  </Step>

  <Step title="Read the record again">
    `GET /api/cad/incidents/{id}`, `GET /api/cad/units`, or `GET /api/cad/me/unit` for a crew.
  </Step>

  <Step title="Show the current state">
    Keep anything the person typed. Tell them the record changed.
  </Step>

  <Step title="Let them decide">
    If they still want the change, send it again with the new `version`.
  </Step>
</Steps>

Argus never replays a conflicting write itself. A success response always means that exact request was applied once.

## Where versions are not used

The civilian, PNC, staff and administration endpoints have no `version` field. They guard against double changes in other ways: a warrant can only be executed while it is active, a marker can only be cleared once, and a second attempt returns a specific 409 such as `WARRANT_NOT_ACTIVE` or `MARKER_NOT_ACTIVE`. Simple field edits there are last-write-wins.

Moderation uses an `Idempotency-Key` header instead, so that retrying a kick or ban can never send it twice.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.