Using JSON Patch
This topic explains how to use JSON Patch to make partial updates against records via our API and includes guidance on each of the six available operations, which are:
Worked examples are drawn from the Bank Details model.
Before you start
JSON Patch is sent as a PATCH request whose body is a JSON array of operation objects. Each operation object has, at minimum, an op and a path; some operations also require a value or a from.
Endpoint
PATCH /BankDetails/{Id}
Required header
Content-Type: application/json-patch+json
The Model (the model used for the examples in this topic)
{
"EmployeeId": "string",
"BankName": "string",
"BankAddress": {
"Address1": "string",
"Address2": "string",
"Address3": "string",
"Address4": "string",
"PostCode": "string"
},
"AccountName": "string",
"AccountNumber": "string",
"SortCode": "string",
"RollNumber": "string"
}
| Field on the model | JSON Pointer path |
|---|---|
| BankName | /BankName |
| AccountNumber | /AccountNumber |
| BankAddress(the whole object) | /BankAddress |
| BankAddress.PostCode | /BankAddress/PostCode |
| BankAddress.Address1 | /BankAddress/Address1 |
add
Sets a value at the target path. If the field already has a value, add overwrites it; if it doesn't exist on the resource yet, add creates it.
Use it when you want to update a previously empty field, for example, adding a roll number for a building society account that didn't have one before.
PATCH /BankDetails/8f3c2e1a-9b44-4d7e-bf21-1a2b3c4d5e6f
Content-Type: application/json-patch+json
[
{ "op": "add", "path": "/RollNumber", "value": "BS-9921-44A" }
]
You can also add an entire nested object in one go:
[
{
"op": "add",
"path": "/BankAddress",
"value": {
"Address1": "1 Pavilion End",
"Address2": "Westgate",
"Address3": "Leeds",
"Address4": "West Yorkshire",
"PostCode": "LS1 4AB"
}
}
]
remove
Deletes the value at the target path. The field is cleared on the resource.
Use it when a piece of information is no longer applicable, for example, the employee has switched from a building society account to a standard bank account and the roll number no longer applies.
[
{ "op": "remove", "path": "/RollNumber" }
]
remove works on nested fields too:
[
{ "op": "remove", "path": "/BankAddress/Address4" }
]
If the path doesn't resolve to a value on the resource, the patch fails — remove does not work as a "delete if exists" operation.
replace
Sets the value at the target path. Unlike add, the path must already exist; otherwise the operation fails.
Use it when the field already has a value and you want to overwrite it. This is the operation you'll reach for most often — updating an account number, sort code, or post code.
[
{ "op": "replace", "path": "/AccountNumber", "value": "12345678" },
{ "op": "replace", "path": "/SortCode", "value": "20-00-00" }
]
Replacing a nested field:
[
{ "op": "replace", "path": "/BankAddress/PostCode", "value": "LS1 5XX" }
]
Multiple operations in a single request are applied in order, and the whole patch is atomic — if any operation fails, none of the changes are committed.
move
Moves a value from one path (from) to another (path). Equivalent to a remove at from followed by an add at path.
Use it when data has been captured in the wrong field and you want to relocate it without re-sending the value. A common case: the city was entered into Address3 but should be in Address2 on this record's address layout.
[
{
"op": "move",
"from": "/BankAddress/Address3",
"path": "/BankAddress/Address2"
}
]
The source field (from) is cleared and the destination (path) takes its value. Both endpoints must be valid JSON Pointers; the source must currently hold a value.
copy
Copies the value at from into path. The source is left untouched.
Use it when you want to duplicate a value across fields without retyping it — for example, setting AccountName to match EmployeeId-derived data already on the record, or duplicating a postcode line into another field during a data tidy-up.
[
{
"op": "copy",
"from": "/BankAddress/Address1",
"path": "/BankAddress/Address2"
}
]
After this patch, Address1 and Address2 hold the same value.
test
Asserts that the value at the target path equals the supplied value. If the assertion holds, the operation is a no-op and the patch continues; if it fails, the entire patch is rejected and no changes are committed.
Use it for optimistic concurrency — guard the update against the field having been changed by someone else since you last read it.
[
{ "op": "test", "path": "/SortCode", "value": "20-00-00" },
{ "op": "replace", "path": "/AccountNumber", "value": "87654321" }
]
In the example above, the account number is only updated if the sort code is still 20-00-00. If another process has already changed the sort code, the test fails and the replace is not applied — the response will indicate the failure and the resource is left exactly as it was.
test can be combined freely with the other five operations to build conditional, atomic patches.
Quick reference
| Operation | Required fields | Effect |
|---|---|---|
| add | path,value | Sets the value atpath, creating the field if needed |
| remove | path | Deletes the value atpath |
| replace | path,value | Overwrites an existing value atpath |
| move | from,path | Moves the value fromfromtopath |
| copy | from,path | Copies the value atfromintopath |
| test | path,value | Asserts equality; fails the whole patch on mismatch |