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

Copy
PATCH /BankDetails/{Id}

Required header

Copy
Content-Type: application/json-patch+json

The Model (the model used for the examples in this topic)

Copy
{

  "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.

Copy

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:

Copy
[

  {

    "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.

Copy

[

  { "op": "remove", "path": "/RollNumber" }

]

remove works on nested fields too:

Copy
[

  { "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.

Copy

[

  { "op": "replace", "path": "/AccountNumber", "value": "12345678" },

  { "op": "replace", "path": "/SortCode",      "value": "20-00-00" }

]

Replacing a nested field:

Copy
[

  { "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.

Copy
[

  {

    "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.

Copy

[

  {

    "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.

Copy
[

  { "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