Ndovu elephant mark ndovuai lab / SCIM: Playgrounds, Specs & Guides / SCIM PATCH, explained

NDOVU · AUDIO EXPLAINER · LISTEN

SCIM PATCH, explained

5:09·6 chapters·transcript
0:00 / 5:09

Keys: space/K play-pause · J/L jump 10s · arrows on the waveform nudge 5s

01 - Chapters

6 chapters

02 - Transcript

Read along

The full briefing transcript, timed to the audio. Click any line to jump there; the current line lights up while you listen.

HostThe PATCH body parsed cleanly. Every JSON type checked out. And the request still failed, with 400 mutability, because the target attribute is read-only.

HostOr the failure is quieter. A deactivation request sets the active flag to the string "False", capital F, and the schema says boolean. It lands as truthy text. Every deprovisioned user stays active, and nothing returns an error. That is the nature of SCIM - S, C, I, M, pronounced sim - and of its PATCH operation in particular: most of its bugs are semantics bugs, not syntax bugs. The body is legal; the meaning is not. This is the walkthrough of Section 3.5.2, the most consequential section of the protocol RFC, op by op.

HostSection 3.5.2 defines a PATCH request as an ordered list of operations: add, remove, and replace. They apply one at a time, each to the result of the previous one. And the whole sequence is atomic: the first failure stops it, and the original resource is restored. A PATCH never half-lands. Three operations in, one fails? Operations one and two roll back with it. That atomicity is the contract, and it is why a server cannot shortcut the evaluation.

HostAdd, without a path, merges at the resource level: a single-valued attribute is replaced, a multi-valued attribute gains the new values, a complex attribute merges sub-attributes. Adding a value that is already present in a multi-valued attribute changes nothing, which is precisely the already-a-member case. Setting one value's primary flag to true resets the flag on the other values, in the same operation.

HostRemove, without a path, fails with noTarget by definition. With a path and a value filter, it loses exactly the matching records, and here is the asymmetry worth memorizing: a remove that matches nothing succeeds, with no change. A replace that filters no record fails noTarget. Removing or emptying a read-only or required attribute fails mutability, and after a successful removal the attribute counts as unassigned, the same null-missing-empty state the filtering rules treat as false.

HostReplace treats a target attribute that does not exist as an add. Replacing a value with null or an empty array clears it. And a complex attribute merges the sub-attributes you supply and leaves the rest alone.

HostEvery attribute carries a mutability characteristic, and it decides which operations the provider must reject. Read-only: every modification fails. Immutable: you may add where no value exists, you may re-assert the identical value for a no-change success, and a different value fails. Required: clearing or removing fails. These four words in the schema predict most 400s before they happen.

HostOne more rule on writes: PUT must not create. Creation is reserved for POST, so a PUT to an unknown identifier answers 404, never 201. And the path that targets all of this has its own grammar: an optional schema URI, the attribute, an optional bracketed value filter, an optional sub-attribute. A path that does not parse fails invalidPath.

HostNow the part that bites in production. Microsoft Entra's provisioning service speaks a documented dialect of PATCH by default: operation values capitalized, Add, Replace, Remove. The boolean active flag serialized as the string "False". Group member removal sent as a value array instead of a path filter. Microsoft's own SCIM compatibility page lists the compliant-behavior row with "Fixed? No, fix date TBD", and the compliant mode sits behind a tenant URL flag.

HostA conforming library has thrown real exceptions on those Entra bodies; there is a recorded case from early 2025. And the lesson generalizes beyond one vendor: a SCIM server in production serves at least two clients, and one of them is usually a provisioning service with a dialect. Accepting the dialect is a provider decision. Knowing you are speaking one is the builder's job, and that review, body against schema, op by op, is exactly what this page's playground exists to run.

HostThree things to carry. First: PATCH is a program, not a write. Ordered operations, each applied to the result of the last, atomic on the first failure; a PATCH never half-lands. Second: the mutability matrix is the predictor. Four words, read-only, immutable, required, read-write, decide which 400s you will get, and PUT never creates. Third: watch for dialects. Capitalized operations and a boolean shipped as the string "False" will not fail at parse time, which is exactly why they survive into production. Build the body, apply it against the schema, and read every divergence; the PATCH builder on this site runs that review for you.

0:00