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

# Optimistic concurrency

> ETag / If-Match prevents lost updates when the API and the NT24 web app edit the same resource concurrently.

Resources that can be edited from multiple channels (API + NT24 web app)
carry an integer `version` that bumps on every modification. The API
surfaces it as an `ETag` header; writes that modify the resource must
include `If-Match` with the last-seen ETag. Mismatch = `409 Conflict`
with the current resource.

## Read → ETag

```bash theme={null}
curl -v "https://api.novatrade24.com/v1/partners/${SELLER_ID}/orders/${ORDER_UUID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"
```

Response headers include:

```
ETag: "5"
```

Body also carries `"version": 5` — same value, two surfaces.

## Write → If-Match

```bash theme={null}
curl -X PATCH "https://api.novatrade24.com/v1/partners/${SELLER_ID}/orders/${ORDER_UUID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'If-Match: "5"' \
  -H "Content-Type: application/json" \
  -d '{ "responsibleContact": { ... } }'
```

| Server state                 | Header          | Result                                                     |
| ---------------------------- | --------------- | ---------------------------------------------------------- |
| `version=5`                  | `If-Match: "5"` | `200 OK`, version bumps to 6                               |
| `version=7` (someone edited) | `If-Match: "5"` | `409 Conflict` with current resource in body               |
| No `If-Match` header         | —               | `400 Bad Request` — header required on versioned resources |

## Conflict resolution

`409` responses carry the **full current resource** in the body. You don't
need a separate GET to reconcile.

```json theme={null}
{
  "type": "https://api.novatrade24.com/problems/version-mismatch",
  "title": "Version Mismatch",
  "status": 409,
  "detail": "If-Match version 5 does not match current version 7.",
  "traceId": "req_...",
  "currentResource": {
    "uuid": "o1p2q3r4-...",
    "version": 7,
    "...": "..."
  }
}
```

Merge your intended change with the current state and retry with the new
ETag:

```typescript theme={null}
async function updateWithRetry(orderId: string, changes: Partial<Order>) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const current = await getOrder(orderId);
    const merged = { ...current, ...changes };

    const r = await patchOrder(orderId, merged, current.version);
    if (r.status === 409) {
      // Another channel edited. Loop with the fresh version.
      continue;
    }
    return r;
  }
  throw new Error('Too many concurrent conflicts');
}
```

## Which resources are versioned

Any resource that NT24 staff can also edit in the web app:

* Buyer (`TradePartner`)
* Order
* Invoice (sub-resource on order)
* Payment (sub-resource on order)
* Transport (pickup/delivery milestones are append-only; see note)

Append-only resources (documents, self-service invitations) don't have
versions. Their mutations create new records or set status fields
exclusively, so concurrency conflicts don't arise.

## Transport milestones — immutable once set

Pickup and delivery confirmations are **not** optimistically concurrent.
They are immutable once set:

* `POST /transport/pickup` twice → second call returns `409` with
  `milestone-already-set` problem type.
* To reverse, use the explicit `POST /transport/pickup/revoke` endpoint.
* Same for `delivery`.

This is stricter than optimistic concurrency because pickup/delivery have
legal consequences (VAT applicability) and should not be "fixed up" via a
silent merge.

## Under the hood

Versions are backed by Hibernate's `@Version` column — platform-wide on
all audited entities. Envers captures every revision for audit trail
(who/when); `@Version` prevents concurrent overwrites at commit time.

## Next

<CardGroup cols={2}>
  <Card title="Error reference" icon="triangle-exclamation" href="/reference/errors">
    `409 version-mismatch` detail.
  </Card>

  <Card title="Idempotency" icon="arrows-rotate" href="/concepts/idempotency">
    Safe retries on writes.
  </Card>
</CardGroup>
