Skip to main content


Unlike insert and update operations, the behavior of upsert can vary significantly across different vendors, if supported at all. This documentation page provides an overview of how our implementation of upsert works.

Details for each upsert operation can be found in the API Reference.


The general structure of a models typically consist of the following fields:

  • id: a unique identifier for the model within the ChannelApe platform.
  • external_ids: a key-value map of external identifiers for the model, such as {"WAREHOUSE_DALLAS_ID": "12345", "SHIPPING_COMPANY_ID": "54321"}.
  • business_id: the unique identifier for the business that owns the model.

When you send a model to us, we first attempt to find an existing model that matches the one you provided. The found match must be unique to be considered usable.

The matching conditions are evaluated in the following order:

  1. id
  2. external_ids
  3. Natural keys


If you include the id field, we will search for a model with that specific identifier. Supplying the id indicates that you already know the model you are looking for. If it doesn't exist, we will create a new model with the supplied id value.

Note ids are GUIDs (globally unique identifiers) and are in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Use a library for your platform to generate these values if you must know the id before creating the model. It's generally not recommended to use this method.

Because id is a globally unique identifier no other search conditions are evaluated if. The rest of the criteria below "fall through" if they don't match.

External IDs

External IDs are a key-value map of external identifiers for the model. You only need to provide a subset of the external IDs. For example, if your model has the following external_ids:

"KEY_1": "value_1",
"KEY_2": "value_2"

You can submit the following input:

"external_ids": {
"KEY_1": "value_1"

The model will match on KEY_1 as the values are equal. However, including multiple keys might cause the model to not be found, for example:

"external_ids": {
"KEY_1": "value_1",
"KEY_2": "new_value"

Would not match as both KEY_1 and KEY_2 must match the values provided.

At the time of writing, external IDs are not supported for all models. Also no guarantee is made that external IDs are unique. The key of external id's is supposed to be the context in which it's used. For example if during inventory processing you need to reference an identifier from your warehouse management system, you could use {"INVENTORY_PROCESSING_ID": "[ID_FROM_WAREHOUSE_MANAGEMENT_SYSTEM]"}. This can only work as well as the id you provide is unique in the context. If the underlying system returns the same id for multiple items, upsert will likely fail to find a unique match.

Natural Keys

Natural keys are a set of fields that uniquely identify a model based on their inherent characteristics. For example, in the context of inventory items, the sku or in the context of locations, the name could be considered natural keys. Although these values are not truly unique in most cases, they are typically unique within a specific context.

Context Values

Any key that identifies a parent resource, also known as a foreign key, is used to narrow down the search domain. For instance, the business_id represents the ID of the business that owns the model. Suppose your account has access to a single business, and you send the following request:

"inventory_item": {
"sku": "sku-123",
"title": "New Title"

In this case, the same inventory item will always be found because the sku is unique per business. However, if your account gains access to another business and sends the same request, there is a chance that the inventory item will be found in the other business. This is because the sku is not globally unique, but unique within each business. To resolve this, include the business_id field:

"inventory_item": {
"business_id": "[target_business_id]",
"sku": "sku-123",
"title": "New Title"

To identify what values constitute a context value look at the post request in the documentation for the model you are trying to upsert. For example, the inventory item has a Note: This is a Foreign Key to next to business_id.

In more complex models, upsert is called from the top down calling other models upsert functions. Setting context values at the top level will propagate to the nested models. For example, setting business_id in the top level of upsert_shipments_full will automatically set the business_id for addresses, locations, shipment items, and so on, when performing their respective upsert operations. It's not suggested to set context values in nested models unless you specifically want to move a model from one business to another.


If no model is found, we will create a new one. The model will be created with the values provided in the request body. All fields marked as required must be provided, except for id.


If a unique model is found, we will update it with the values provided in the request body. In the update scenario all fields are optional except for identifier information discussed in search.

By default the model will be patched. This means that only the fields you provide will be updated. If you want to replace the entire model, you can set the patch field to false. This will replace the entire model with the one you send us. Do note that this will remove any fields you don't send us.

The exception to this rule is any required fields will be copied over from the existing model.

External Ids

Regardless of that patch value, external IDs will always be patched and merged with the existing external IDs. On key collision, the value in the request will overwrite the existing value.

If you really want to replace the entire external IDs map, use the patch method provided by the rest api directly or mutate via the graphql api.


Regardless of whether a model was inserted or updated, the response will always be the entire model as it's stored in the system. The result is identical to calling the post method on the rest api directly.

Best Practices

If you have the id of an existing model, use it. This requires no further context and is the most efficient way to upsert a model.

If you don't, use the external_ids field and provide as many context identifiers as possible. At the very minimum business_id should be provided as well.

In some cases natural keys are better than external IDs. By there very nature this should be apparent although efforts in documenting this by function are ongoing.

Additional Notes


Upsert is a non destructive operation, tt will never delete a model. This may change in the future but it'll be opt-in with a delete flag.

Upsert is also acid compliant, if the request fails, no changes will be made to the system regardless of the complexity of the model.


Upsert has no knowledge of the current permissions of the user executing it. It also doesn't have elevated permissions. This means that it can only select, create and update models that the executing user has access to. It'll also blindly attempt to do these operations on your behalf failing the operation if you don't have the required permissions.


Always keep in mind that upsert is a best effort operation. If you provide sufficient information to uniquely identify a model the operation will generally behave as expected.