Every 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 the ETag. Every write returns one, and so does a read on the surfaces that support updates:
2. Send it back as If-Match:
3. Keep the new 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.
Do not drop the header to get past a 412. The header is not the obstacle — the concurrent edit is. Removing it does not make your change correct; it makes it overwrite whatever you did not see, which is the exact outcome the precondition exists to prevent.
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.
Retrying a 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.
On the accessorial and credit surfaces, the customer, carrier, and driver accessorial and credit updates are replacements; the driver credit update is a merge.
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.
Moving money to a different parent is a delete and a create, not an edit — the new parent settles differently, and rewriting the pointer would leave the original statement wrong.
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 takes ExternalId, an update takes If-Match.
See Idempotency for retrying creates safely.