General concepts
This document gives an overview of the characteristics and common features of the API.
The commercetools Composable Commerce HTTP API offers an interface for programmatic access to the data stored and associated functionality. While you can use the API directly for experimentation or custom integration solutions, we recommend you look at the available client libraries and SDKs, which provide a more comfortable development experience.
Regions
To provide the best possible Service Level Agreement (SLA) and minimal latency, the Composable Commerce API is provided in the following Regions: North America (Google Cloud, Iowa), North America (AWS, Ohio), Europe (Google Cloud, Belgium), Europe (AWS, Frankfurt), and Australia (Google Cloud, Sydney).
These Regions are completely isolated from each other, and no data is transferred between them.
User accounts
User accounts created for one Region are not valid for other Regions. A signup is required for each individual Region.
If you need help choosing a Region to locate your Project in, contact the Composable Commerce support team.
Hosts
The Composable Commerce HTTP API is hosted at the following URLs:
Region | API URL |
---|---|
North America (Google Cloud, Iowa) | https://api.us-central1.gcp.commercetools.com/ |
North America (AWS, Ohio) | https://api.us-east-2.aws.commercetools.com/ |
Europe (Google Cloud, Belgium) | https://api.europe-west1.gcp.commercetools.com/ |
Europe (AWS, Frankfurt) | https://api.eu-central-1.aws.commercetools.com/ |
Australia (Google Cloud, Sydney) | https://api.australia-southeast1.gcp.commercetools.com/ |
Prepend all requests to endpoints with these URLs.
If the documentation refers to https://api.{region}.commercetools.com
, replace the {region}
placeholder with the actual value.
The GraphQL, Import API, and Change History endpoints use different hosts, which are listed with each endpoint.
Although deprecated hostnames like api.sphere.io
(Google Cloud, Belgium), api.commercetools.com
(Google Cloud, Belgium), and api.commercetools.co
(Google Cloud, Iowa) remain intact as aliases, we strongly encourage changing your configuration the new hostname structure.
Disallowed requests
Requests to the HTTP API are blocked if any of the following conditions are true:
- The combination of request URL and headers is longer than about 15KB.
- The request method does not allow a body, but the request has one.
- The request contains an upgrade header.
- The HTTP version is unknown.
We strongly recommend using the host and domain names in your applications instead of hardcoding any IP address of the API hosts. IP addresses can change at any time without prior notice.
UTF-8
All text content storage, processing, and API interaction use UTF-8 encoding.
JSON
The majority of the HTTP API endpoints consume and produce standard application/json
payloads. To ensure that payloads are encoded correctly, use Content-type: application/json
in your HTTP header when sending POST
requests.
When consuming a JSON response, ignore any unrecognized fields in JSON objects of responses, or deal with them such that it does not cause the client application to crash. The API assumes clients behave properly and considers the addition of new fields to be backward-compatible for existing clients. In addition, client applications should not rely on the presence of undocumented fields. Fields returned by an undocumented endpoint can be renamed or removed at any time without prior notice.
Identifier
Some resources contain an id
field that uniquely identifies a resource. The ID is generated by the Composable Commerce API when creating a resource.
Some resources have a string field key
, which also uniquely identifies a resource. The key
identifier is always assigned to a resource by the client.
Import and export functionalities, including the Import API, use the key
field to identify resources. You must assign a key
to resources to update them by import.
Update guarantees
The API provides different update guarantees for different kinds of requests.
Strong consistency
Each resource offers read-after-write consistency. The read-after-write consistency implies that when you update an entity, wait for the success response, and then read the same entity for your changes to be visible.
If a server-side problem occurs, indicated by a 500 Internal Server Error HTTP response, the requested action may still successfully complete after the error is returned. If you receive this error, you should verify the status of the requested resource.
When new aggregates are being created there is no general solution to verify the status of the resource. Querying for a supplied unique identifier can help. For example, if you are creating an Order, you could query using the Order number that you supplied during the creation request.
Eventual consistency
Some changes are not visible immediately after the update. For example, if you update a discount, updating the discount itself provides read-after-write consistency, but the prices on all affected Products are updated after a delay. When you read a price immediately after the discount, you may read an old value and not the updated one. If you read the price again after a while, you will read the new value.
Similarly, after updating a Product the Product Projection Search index is updated after a delay. If you retrieve a search result immediately after an update, the result may not reflect the updated Product. When trying later, the search index will be updated.
These delayed updates provide an Eventual Consistency
guarantee. It means that after a delay, what you can read reflects all the past updates.
These delays can vary depending on the amount of data to update. We try to keep these delays as short as possible.
Optimistic concurrency control
Many API resources use optimistic concurrency control to prevent lost updates when making changes to existing data concurrently.
These resources have a version
attribute.
When sending (partial updates) to these resources, send the expected version of a resource as a part of the request.
After making an update, the HTTP response body contains the new version of the resource. An API client must wait for the latest version before sending follow-up requests.
Background processes and other events can also change a resource's version without any requests sent from an API client directly to the resource. Do not rely on a consistently incremented version number.
The API does not use ETag
and If-Match
HTTP headers
for the purpose of optimistic concurrency control.
If a version mismatch occurs, the API returns a ConcurrentModification error.
Security
All API access is over HTTPS. API authorization uses OAuth 2.0 bearer tokens. For more information, see Authorization.
All HTTPS traffic is secured through Transport Layer Security (TLS). The relevant certificates are signed by trusted authorities. At any point in time, we will rely on a trusted root certificate. It is the API user's obligation to keep their systems updated regarding SSL/TLS capabilities as well as trusted root certificates. The server certificate, as well as intermediary certificates of the certificate chain, are transmitted as part of the TLS handshake. The API user is responsible for checking the expiration dates of all certificates in the chain and renewing them whenever necessary.
Please note that it is possible to connect to the API via HTTP (without SSL/TLS). You will, however, receive a 404
error, including an HTML body response on every attempt.
It is not necessary to support the full set of SSL/TLS features listed here (or stop supporting other features), especially the cipher suites, as long as one valid combination exists. The TLS handshake includes a "negotiation phase" that chooses the best combination that both client and server support.
Be aware that the certificates may at times support more features in transition periods. However, only the specifications listed below are reliable. You must not rely on other SSL/TLS features even if they currently work.
- Ciphersuites:
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
- Versions: TLS 1.2, TLS 1.3
Query features
Common query features include sorting, paging, and Reference Expansion. Certain aspects of these features are standardized across all parts of the API, as described in the following sections.
To filter results, Query Predicates allow you to express complex queries on many resources.
Single resource query response
Query responses for single resources only return a single, top-level JSON object.
PagedQueryResult
For query responses of requests supporting paging via limit
and offset
, the following common
top-level fields are returned:
offset
- Number
The offset supplied by the client or the server default. It is the number of elements skipped, not a page number.limit
- Numbercount
- Number
The actual number of results returned inresults
.total
- Number - Optional
The total number of results matching the query. This number is an estimation that is not strongly consistent.
This field is returned by default.
For improved performance, calculating this field can be deactivated by using the query parameterwithTotal=false
.
When the results are filtered with a Query Predicate, thetotal
is limited to the maximumoffset
of10 000
.results
- Array of resultsfacets
- Object containing FacetResults - Optionalmeta
- Object containing supplementary information about the results - Optional
Sorting
A query endpoint that supports sorting does so through the sort
query parameter.
For sorting based on values of standard fields of resources the provided parameter value must be a valid sort expression of the form:
<field name> <sort direction>
For sorting based on values of LocalizedString fields, use the field name followed by the Locale:
<field name>.<locale> <sort direction>
For sorting based on values of Custom Fields, use the expression:
custom.fields.<field name> <sort direction>
The default sort direction is ASC
. The allowed sort paths are typically listed on the
specific query endpoints.
Find below some examples of sort expressions:
key ascname.en asccustom.fields.trackingNumber desc
If multiple sort expressions are specified via multiple sort
parameters, they are combined into a composed sort where the results are first sorted
by the first expression, followed by equal values being sorted according to the second expression, and so on.
The sorting is case-sensitive.
Paging
Queries on collections of resources can optionally provide paging functionality through limit
and offset
query parameters, allowing clients to request a certain page of the query result.
Limit
You can set the maximum number of results to return from a query using the limit
query parameter. Allowed is a value between 0 and 500
. The default limit
on most query endpoints is 20.
Setting a high limit can impact performance. For more information, see our performance tips.
Offset
The offset into the results matching the query can be set using the offset
query parameter. The default value is 0, indicating that no results should be skipped. The maximum offset is 10 000
.
Total
The value of total
is limited to the maximum offset
when the results are filtered with a Query Predicate.
To improve query performance, the calculation of the total
field in the PagedQueryResult should be deactivated by passing false
in the withTotal
query parameter whenever possible.
Iterating over all elements
When iterating over all elements from an endpoint (for example to modify or export all Products), using offset is not suitable to page through results:
offset
has an upper limit (see above), so exports can later become incomplete when the number of resources grows.- When an element is inserted while iterating over pages, a request with the next offset value might return already processed elements.
- Using large offsets can lead to slower response times.
To iterate over all elements while avoiding offset
, you should follow this alternative approach instead:
- Query for a limited number of results in which the elements are sorted by ID. Do not request the
total
field by passingwithTotal=false
. - To retrieve the next chunk of elements, iterate with the same query by filtering for IDs greater than the last ID retrieved from the previous step.
- Repeat step 2 as long as the number of returned results equals the requested limit.
The following pseudocode demonstrates the approach using chunks of 100 elements at each iteration:
lastId = nullcontinue = truewhile (continue) {if (lastId == null)response = client.get(endpoint + "?withTotal=false&limit=100&sort=id+asc")elseresponse = client.get(endpoint + "?withTotal=false&limit=100&sort=id+asc&where=id%3E" + lastId) // "id%3E" is "id >" encodedresults = response.resultscontinue = (results.size == 100)lastId = results.last.id// do something with the result}
Resource timestamps
Many resources exposed on the API contain the createdAt
and the lastModifiedAt
timestamps.
Those timestamps are automatically set by the server on creation or modification of the resource. They can be used as predicates and for sorting in queries.
Reference Expansion
Reference Expansion is a feature of the endpoints listed in the section below that enables clients to request server-side expansion of Reference resources, thereby reducing the number of required client-server roundtrips to obtain the data that a client needs for a specific use-case.
Reference Expansion can be used when creating, updating, querying, and deleting these resources.
Expansion of references does not cause requests to fail even if not all of the references can be expanded (for example, because the referenced resource cannot be found). The server will try to expand as much as possible but never beyond what the client requested. It is up to the client to decide which referenced resources it deems critical for completing a specific use-case, for example, by either treating it as an error or by just not rendering/returning the missing information.
Endpoints supporting Reference Expansion
API endpoints that support Reference Expansion provide an expand
query parameter, which can be used to specify one or more expansion paths. For specifying several expansion paths in the same request, use the expand
query parameter several times with different parameter values.
Expansion paths
Expansion paths define the full path to the reference in a resource. Expansion path format:
refFieldName
- expands a reference field namedrefFieldName
located at the root of the resourcerefArrayFieldName[*]
- expands all references contained in the array of references namedrefArrayFieldName
refArrayFieldName[0]
- expands the first reference in the array of references namedrefArrayFieldName
objFieldName.refFieldName
- expands the referencerefFieldName
contained in the objectobjFieldName
objArrayFieldName[*].refFieldName
- expands the referencerefFieldName
in all objects contained in the arrayobjArrayFieldName
objNestedArrayFieldName[*][*].refFieldName
- expands the referencerefFieldName
in all objects contained in the nested arrayobjNestedArrayFieldName
The expansion path can also point to references in expanded objects. When referencing a resource within an expanded object, do not include the obj
field within the expansion path. For example, to expand the Customer Group of a Customer in a ShoppingList, use customer.customerGroup
.
GET https://api.{region}.commercetools.com/<project-key>/shopping-lists/<example-shopping-list-id>?expand=customer.customerGroup
Examples for Reference Expansion
Product Type and Tax Category on a Product
The JSON snippet below is taken from an Example-Product
representation that contains two references: one to an Example-Product-Type
and one to an Example-TaxCategory
. Without Reference Expansion you will get such a representation as response from the endpoints:
{"id": "<example-product-id>","version": 4,"productType": {"typeId": "product-type","id": "<example-product-type-id>"},..."taxCategory": {"typeId": "tax-category","id": "<example-tax-category-id>"},...}
With Reference Expansion you will get the expanded objects embedded into the product by an additional obj
field as shown in the following JSON snippet:
{"id": "<example-product-id>","version": 4,"productType": {"typeId": "product-type","id": "<example-product-type-id>""obj": {"id": "<example-product-type-id>","version": 4,"name": "Example Product Type","description": "example Product Type to showcase Reference Expansion","classifier": "Complex","attributes": [....],},..."taxCategory": {"typeId": "tax-category","id": "<tax-category-id>""obj": {"id": "<tax-category-id>","version": 2,"name": "Standard tax category","rates": [{"name": "5% US","amount": 0.05,"includedInPrice": false,"country": "US","id": "zJJ5KIGH"},{"name": "19% MwSt","amount": 0.19,"includedInPrice": true,"country": "DE","id": "893f_nXJ"}],...}
The query for the Example Product
introduced before - including both expansion paths: productType
and taxCategory
- would look like this:
GET https://api.{region}.commercetools.com/<project-key>/products/<example-product-id>?expand=productType&expand=taxCategory
Categories on a Product
Expanding all Categories within a Product is an example for an expansion path for References in arrays: masterData.current.categories[*]
.
The query would thus, look like this:
GET https://api.{region}.commercetools.com/<project-key>/products/<example-product-id>?expand=masterData.current.categories%5B*%5D
To get the parent Category of the Product's categories expanded, use masterData.current.categories[*].parent
as expansion path.
ReferenceType Attributes on a Product
In case you want to have the resources expanded that are the values on the AttributeReferenceType Attributes, you need to specify two expansion paths (one for the masterVariant
and one for the other variants
on the Product) to get the expanded objects on all the Product Variants. The example below specifies the expansion path for the current
ProductCatalogData, but it works similarly for the staged
catalog data.
masterData.current.masterVariant.attributes[*].value
masterData.current.variants[*].attributes[*].value
GET https://api.{region}.commercetools.com/<project-key>/products/<example-product-id>?expand=masterData.current.masterVariant.attributes%5B*%5D.value&expand=masterData.current.variants%5B*%5D.attributes%5B*%5D.value
For AttributeSetType of AttributeReferenceType Attributes, you will get all the attribute values expanded when appending the value
field in the expansion path with a wildcard [*]
, like so:
masterData.current.masterVariant.attributes[*].value[*]
masterData.current.variants[*].attributes[*].value[*]
Line Item States on an Order
The ItemStates of LineItems in an Order can be expanded with the expansion path: lineItems[*].state[*].state
.
GET https://api.{region}.commercetools.com/<project-key>/orders/<example-order-id>?expand=lineItems%5B*%5D.state%5B*%5D.state
ReferenceType Custom Fields
To expand the resources that are the values of a ReferenceType Custom Field, expand the Custom Field. For example, custom.fields.nameOfCustomField
.
GET https://api.{region}.commercetools.com/<project-key>/categories/<example-category-id>?expand=custom.fields.nameOfCustomField
If the Custom Field contains a set of ReferenceTypes, expand it like an array. For example, custom.fields.nameOfCustomFieldSet[*]
GET https://api.{region}.commercetools.com/<project-key>/categories/<example-category-id>?expand=custom.fields.nameOfCustomFieldSet%5B*%5D
Include custom data by expanding Custom Objects
Custom Objects have a value
field, which can contain any JSON value. You can associate data stored in Custom Objects to Products and other resources and retrieve this data using reference expansion on the reference type Attribute or Custom Field. With this approach, you can query for the custom data associated to the resource without the need for Custom Objects scopes.
To reference a Custom Object in a Product, its Product Type must have an AttributeReferenceType Attribute that references key-value-document
. To expand this Attribute, see ReferenceType Attributes on a Product.
For other entities, create a Type that defines a ReferenceType Custom Field that references key-value-document
. You can then assign values for this Custom Field to entities using the custom
field in the draft, or the Set Custom Type update action. To expand this Custom Field, see ReferenceType Custom Fields.
Cleanup of old data
Refresh tokens and resources such as Carts, Orders, Payments, and Shopping Lists are created frequently. If kept forever, they can negatively influence the performance of your Project and the time to restore it from a backup. Hence, unused data must be deleted or old data must be moved to cheaper storage for archival (for example, blob storage such as AWS S3 or Google Cloud Storage) to ensure the best performance of your Project.
Automatic cleanup
To maintain optimal performance, an automatic clean-up task runs in the background. This task deletes resources after a set number of inactive days; however, the deletion timer resets with each modification to the resource.
While default retention periods exist for each affected resource type, you have the flexibility to modify these durations based on your specific needs. For example, Carts have a default retention period of 90 days. You might decide to keep the 90-day default for anonymous Carts, but then override the value for individual Carts of logged-in customers, allowing them to persist for longer. Similarly, you could keep the 360-day default for the removal of Shopping Lists but set a lengthier duration for Shopping Lists that have been publicly shared.
Additionally, if your Project contains more than the allowed limit of Carts, Shopping Lists, or refresh tokens, the least recently modified resources are automatically deleted.
The following table details the default retention period for each affected resource type and how the defaults can be adjusted.
Resource type | Default retention period | Configure the retention period |
---|---|---|
Refresh tokens | 200 days | To change the default expiration time, configure the refresh token validity for each API Client. |
Carts | 90 days | To change the Project default, use the Change Carts configuration update action. To change the default for individual resources, use the Set DeleteDaysAfterLastModification update action. |
Shopping Lists | 360 days | To change the Project default, use the Change ShoppingLists configuration update action. To change the default for individual resources, use the Set DeleteDaysAfterLastModification update action. |
Messages | 15 days, if enabled | To change the Project default, use the Change Messages Configuration update action. |
Create carts and anonymous sessions only when necessary
Creating a new cart or an anonymous session on the first page visit can result in a very high number of resources, for example, if a stateless web crawler acts like a new Customer on every request or if a lot of visitors drop off after one or two page views. To avoid creating unused resources, only create them when needed (for example, when the visitor is adding a Product to the cart).
Partial updates
For partial updates to existing resources, a common approach used by the HTTP API is
to accept a custom patch format in the form of a list of resource-specific actions that
need to be processed. The request method for partial update requests is usually POST
,
but PATCH
might also be supported in the future.
The general request representation (patch format) of a partial update request is as follows:
id
- String - Optional if part of the URL.
The ID of the targeted resource.version
- Number - Required if the targeted resource is versioned.
The expected current version of the resource on which to apply the changes.actions
- Array of resource-specific actions - RequiredMisspelled optional fields within the
actions
array are treated as additional fields and ignored by the API. In particular, no error is sent back.
Example Request:
{"id": "a136fd9e-bc29-4d05-813f-350b248aefd8","version": 1,"actions": [{"action": "setTitle","title": "My title"},{"action": "setText","text": "My text"}]}
The concrete actions that can be used on a specific resource are documented along with the resource.
All actions provided in a specific resource's patch format are generally freely combinable in a single request. Furthermore, such update requests only succeed or fail as a whole, meaning, unless explicitly stated otherwise, the result of all actions sent in the request is applied to a resource or none at all, if any of the actions fail.
Not all updates or state transitions of a resource may be freely composable with one another in a single HTTP request. For those updates, other endpoints may be provided to handle only a specific update or state transition in a single HTTP request.
Correlation ID
A correlation ID is a unique identifier of a single action or event (such as checking out a cart), allowing to trace the action across multiple systems. For example, a mobile app sends a request to its backend, that backend makes another request to the Composable Commerce API, which makes yet another request to an API Extension or external OAuth server. If the mobile app sets a Correlation ID, and the backend forwards it to the Composable Commerce API, it'll also be forwarded to the API Extension or external OAuth server. Therefore, the action can be followed through the logs of all four systems.
correlation IDs are strings that may only contain alphanumeric characters, underscores, and hyphens and have a length of 8 to 256 characters. UUIDs can be used to ensure uniqueness.
The HTTP header X-Correlation-ID
is used to propagate the correlation ID on HTTP requests and responses. If a client does not provide a correlation ID for an HTTP request, the Composable Commerce API generates one.
When contacting support regarding a specific request, please also include the correlation ID as it allows us to look up the request more easily.
Client logging BETA
Resources within commercetools Composable Commerce provide information on changes and modifications in the createdAt
, createdBy
BETA, lastModifiedAt
, and lastModifiedBy
BETA fields.
The createdBy
BETA field is present on resources created after 1 February 2019. The lastModifiedBy
BETA field is present on resources created and/or updated after 1 February 2019.
Events tracked
When you create a resource, the createdBy
BETA, createdAt
, lastModifiedBy
BETA, and lastModifiedAt
fields are added.
When you call an update action using an API Client, the lastModifiedBy
BETA and lastModifiedAt
fields are updated. However, if the updated resource does not differ from the current resource, the action might be skipped and these fields are not updated. An API Client update refers to any modifications made through a client application interacting with either the HTTP or GraphQL API, or the Merchant Center.
Some modifications can be made indirectly, for example, via an import. This change will be tracked in the attributedTo
field of either the createdBy
or lastModifiedBy
fields.
If modifications are done without using an API call or the Merchant Center, the lastModifiedBy
BETA field is not updated. In some cases, only the lastModifiedAt
field is updated.
For example, activating a Product Discount updates the Price of a Product. As this update is triggered as a background process, and not by an API call or the Merchant Center, the Price update is not tracked.
Information in the fields
The createdBy
BETA and lastModifiedBy
BETA fields do not contain any personally identifiable information.
However, they can contain an external user ID, reference to the Customer ID, or identifier for an anonymous session in optional fields. The fields themselves are JSON objects. For more information, see the CreatedBy and LastModifiedBy common types.
External user IDs
To associate an external user ID with a modification, API Clients can use the X-External-User-ID
HTTP header. It can be useful for tracking changes made by users in an external service. For example, if you do not use the Merchant Center or the API authorization flows, using the X-External-User-ID
HTTP header can provide more information in client logging fields than might otherwise be available.
The externalUserId
field returns information passed in this header, and is present on most representations that are passed to your front-end applications. Do not pass personal information, such as user email addresses to the X-External-User-ID
header.
When using the X-External-User-ID
header, it is your responsibility to encrypt any information passed to the header or be GDPR and security-compliant.
To use external user IDs from an external OAuth server, provide an external OAuth token with external_user_id:{externalId}
in the scope. This ID is then used in the externalUserId
field.