Oracle Fusion REST API ETag and If-Match: Fixing 412 Precondition Failed
A PATCH that worked yesterday returns 412 Precondition Failed today, with no payload change on your end. Nothing is broken — Oracle Fusion REST APIs use optimistic concurrency control, and the 412 is the API telling you the record moved since you last read it. The mechanism is ETag and If-Match, and it applies to essentially every PATCH and PUT across HCM and FSCM, yet the top search results for it are generic HTTP-status-code explainer sites with zero Oracle-specific detail. This is the practical version: where the ETag comes from, how to send it back, and how to handle the 412 when it happens.
All examples use an anonymized pod (acme.fa.us2.oraclecloud.com) and placeholder identifiers — swap in your own.
Why Oracle uses ETags at all
Two integrations updating the same worker or the same invoice at the same time is a real scenario, not an edge case — a scheduled sync job and a user editing the same record in the UI, or two integration jobs racing after a retry. Without a concurrency check, whichever PATCH lands last silently overwrites the other, and nobody gets an error. ETag-based concurrency turns that into a loud, recoverable failure instead of a quiet, unrecoverable one.
The flow, per Oracle’s REST API documentation for Common Features:
GETa resource. The response includes anETagheader — an opaque version token for that exact resource state.- When you
PATCHorPUTthe same resource, send that token back in anIf-Matchheader. - Oracle compares your
If-Matchvalue against the resource’s current ETag. If they match, the write proceeds and a fresh ETag comes back. If they don’t match — someone else changed the record since your GET — the write is rejected with412 Precondition Failedand the response carries the current ETag.
Reading the ETag
curl -i -u integration.user \
"https://acme.fa.us2.oraclecloud.com/hcmRestApi/resources/11.13.18.05/workers?q=PersonNumber=100001"
Response headers include something like:
ETag: "AAAAAAAAAAA="
Capture that exact string, quotes included — it’s compared byte-for-byte, not parsed.
Sending it back on PATCH
A worker location update, using the same LocationCode field from our effective dates guide:
curl -u integration.user -X PATCH \
-H 'Content-Type: application/json' \
-H 'If-Match: "AAAAAAAAAAA="' \
-d '{"LocationCode": "HQ_LONDON"}' \
"https://acme.fa.us2.oraclecloud.com/hcmRestApi/resources/11.13.18.05/workers/00020000000EACED.../child/workRelationships/.../assignments/..."
Same pattern on the FSCM side — updating a receivables credit memo header:
curl -u integration.user -X PATCH \
-H 'Content-Type: application/json' \
-H 'If-Match: "BBBBBBBBBBB="' \
-d '{"TransactionDate": "2026-08-01"}' \
"https://acme.fa.us2.oraclecloud.com/fscmRestApi/resources/11.13.18.05/receivablesCreditMemos/300000001234567"
If the ETag matches the server’s current value, the PATCH applies and the response returns a new ETag — use that one for your next write, not the one you started with.
Handling 412 Precondition Failed
When the tokens don’t match, the response is:
HTTP/1.1 412 Precondition Failed
The correct recovery is not to retry the same request — it will fail again for the same reason. Instead:
GETthe resource again to see the current state and the current ETag.- Reconcile: is your change still valid against the new state, or did the conflicting update already do what you needed?
- Re-issue the PATCH with the fresh ETag in
If-Match.
For automated integrations, wrap the GET → PATCH sequence in a retry loop with a small cap (2–3 attempts) rather than looping indefinitely — a 412 that keeps recurring usually means two processes are fighting over the same record on a schedule, which is a design problem, not a token problem.
Common gotchas
- Treating 412 like a generic error and retrying the identical request. It will 412 again. The fix requires a fresh GET, not a resend.
- Reusing a stale ETag across multiple writes. Each successful write returns a new ETag. If you PATCH twice in a row using the ETag from the first GET, the second PATCH will 412 even though nothing external changed — your own first write moved the token.
- Dropping the quotes. The
If-Matchvalue is the full quoted string from theETagresponse header, not the text inside the quotes. - Skipping
If-Matchentirely. Some resources still accept the write without it and just apply the last-write-wins behavior you were trying to avoid; others reject the request outright. Don’t rely on the header being optional just because one endpoint tolerated its absence. - Caching an ETag across a long-running batch job. If a batch reads a resource, does other work, and PATCHes it minutes later, treat that ETag as likely stale — re-GET immediately before the write if the gap is more than trivial.
Where this fits with everything else
If-Match is orthogonal to the other REST mechanics we’ve covered: it doesn’t change how you build q filters or finders, and it applies regardless of the REST-Framework-Version header discussed in our Postman setup guide. It’s purely a write-path safety check — GETs never need it, and only PATCH/PUT (and some DELETE operations) are affected. For the rest of the request lifecycle — auth, pagination, expand — see the HCM REST API examples post and the full endpoint catalog.
This post is part of our complete Oracle Fusion API guide — auth, base URLs, q filters, finders, and key endpoints in one place.