PATCH on the Alvys Public API is guarded by an ETag. This is the general mechanism, not a per-endpoint quirk: if a surface supports PATCH, it expects a precondition.
The problem it solves is the one blind updates create. You read a record, decide a change, and send it. Between your read and your write, someone else changed the same record — a dispatcher in the UI, another integration, a second worker of your own. Without a precondition your write lands on top of theirs, and their edit is gone with no error and no trace.
The loop
1. Read, and keep theETag. Every write returns one, and so does a read on the surfaces that support updates:
If-Match:
ETag from the response. Each successful update issues a new one, and the next update needs it. A flow that updates twice can carry the value forward instead of re-reading in between.
What the answers mean
412 and 409 are different failures and want different handling. A 412 says try again with fresh information; a 409 says this record is closed, stop asking.
Handling a 412 properly
A 412 is not a retry-the-same-bytes situation. The record changed, so re-send only after deciding your change still applies:
- Re-read the record and take the new
ETag. - Compare it against what you expected. Did the other writer change the same field you are changing, or a different one?
- If your change still makes sense, apply it to the new state and send with the new validator.
- If it does not — someone already corrected the amount you were about to correct — stop. Do not re-apply it.
412 in a loop without step 2 is a blind update with extra steps.
Replacement versus merge
Read the endpoint before assuming which one you are calling, because the two behave differently on an omitted field:- Replacement — the update writes all the mutable fields. Omitting one clears it. Send the complete set you want the record to end up with, not only what you are changing.
- Merge — the update writes only what you send, and leaves the rest alone. Send at least one field.
Custom references on loads and stops
Custom reference writes follow the same precondition rules, with one detail worth knowing before you build the flow. The same rules apply to trip references as to stop references.- Creating a reference does not take a precondition. Send the write without
If-Match. - Updating an existing reference does. Send the
ETagof the record that owns it. - For a load reference, that is the load’s
ETag. - For a trip reference, that is the trip’s
ETag. Read it from Get trip or from a prior create/update response. - For a stop reference, that is also the trip’s
ETag, not a per-stop one. Reading a trip, its stop list, or a single stop returns the tripETag, and that is the value the stop reference update expects. - Create and update return a refreshed
ETag(loadETagfor load writes, tripETagfor trip and stop writes), so a multi-write flow can carry the value forward without re-reading between steps.
409 Conflict: a record holds one instance of each catalog definition. On this surface that means a duplicate catalog definition on the same load, trip, or stop — not a settled-money conflict. Still do not retry it; update the existing reference instance instead.
What updates never change
Structural fields are fixed when a record is created and are not accepted on an update:- An accessorial or credit cannot move to a different load, trip, stop, or accessorial type.
- A driver credit cannot move to a different driver or owner-operator.
This is not idempotency
An ETag protects a record that already exists from a concurrent edit. An idempotency key protects a record that may not exist yet from being created twice. They solve different problems and are not interchangeable — a create takesExternalId, an update takes If-Match.
See Idempotency for retrying creates safely.