JSON Patch

To help consumers update resources in as specific and intentional way as possible, we've begun rolling out endpoints that support "JSON Patch" as described by IETF RFC 6902. A JSON Patch request allows you to describe changes to be made to a resource rather than sending the full definition of a resource.

More details about JSON Patch (including links to useful libraries) can be found at https://jsonpatch.com/

Simple Example

The original resource:

{
    "customer": {
        "name": "Jim Gordon",
        "email": "[email protected]"
    },
    "orders": [
        {
            "orderNumber": 121,
            "amount": 123
        },
        {
            "orderNumber": 122,
            "amount": 37
        }
    ]
}

The JSON Patch:

[
    {
        "op": "replace",
        "path": "/customer/email",
        "value": "[email protected]"
    },
    {
        "op": "add",
        "path": "/orders",
        "value": {
            "orderNumber": 123,
            "amount": 42
        }
    }
]

The final document:

{
    "customer": {
        "name": "Jim Gordon",
        "email": "[email protected]"
    },
    "orders": [
        {
            "orderNumber": 121,
            "amount": 123
        },
        {
            "orderNumber": 122,
            "amount": 37
        },
        {
            "orderNumber": 123,
            "amount": 42
        }
    ]
}

EveryAction 8 Requirements

We've added a few requirements on top of standard JSON Patch that help to make sure consumers are updating exactly the things they're interested in updating.

Array Pointer Tests

JSON Patch uses JSON Pointers which can point to specific items in a collection using zero-based indexing. In the example above, we could refer to the amount of Order Number 122 as /orders/1/amount. Because sort order isn't always guaranteed nor can we guarantee another user won't add another item to a collection between the time you determine the array index and make the PATCH call, we require all such operations to be accompanied by a test operation that verifies a value at the target.

In the example above, if we wanted to change the amount of Order Number 122, we'd send two operations. The first tests a unique value on the object at position /orders/1 to verify it's the one we want to update. The second changes the value of amount.

[
    {
        "op": "test",
        "path": "/orders/1/orderNumber",
        "value": 122
    },
    {
        "op": "replace",
        "path": "/orders/1/amount",
        "value": 78
    }
]

🚧

Operations are sequential

If you're using array pointers and you remove an item, keep in mind that the array index of all remaining items will change.

  • test value at position 0
  • remove item at position 0
  • replace item at position 0 -- was previously the item at position 1

Content-Type Header

Because JSON Patch documents are valid JSON, we will continue to accept application/json as the Content-Type. We will ignore application/json-patch+json.

Not all properties are patch-able

Not all properties of an object are patch-able. And the properties that are patchable may vary depending on the endpoint. For example, you can change the Campaign of Code by patching /campaign/campaignId but you can't use the same endpoint to change the name of that campaign.

The properties that are patchable will be listed in the individual endpoint's documentation.

Errors

You could come across a couple JSON Patch specific errors. If you look at the hint property of the error you'll get more information about what needs to be done to remedy the issue.

Does not exist or can't be patched

If you try to patch a property that we haven't enabled patching for, you'll get a validation error like:

{
	"errors": [
		{
			"code": "INVALID_PARAMETER",
			"text": "'path' '/supportedEntities/0/name' does not exist or cannot be patched in this context.",
			"properties": [
				"operations[0].path"
			]
		}
	]
}

Must also specify a test op

If you're trying to patch a property with an array index but don't include a test operation (described above), you'll get a validation error like:

{
	"errors": [
		{
			"code": "INVALID_PARAMETER",
			"text": "When using the 'replace' op on '/supportedEntities/0/name' in 'path', you must also specify a corresponding 'test' op for '/supportedEntities/0'",
			"properties": [
				"operations[0].path"
			],
			"hint": "Using a 'test' ensures that the position of the item you intend to modify has not changed positions."
		}
	]
}

Not equal to the test value

If you include a test operation (described above) and the test fails, you'll get a validation error like:

{
	"errors": [
		{
			"code": "INVALID_PARAMETER",
			"text": "The current value 'Cats' at path 'name' is not equal to the test value 'Dogs'.",
			"properties": [
				"operations[0]"
			]
		}
	]
}