Extend the behavior of an API with your business logic.
100
additional updates to it.service
applications using Connect.An API Extension is applied to API calls from all clients, including those provided by commercetools, like the Merchant Center.
API Extensions are available for the following resources:
- Carts
- Orders
- Payments
- Customers
- Customer Groups
- Quote Requests
- Staged Quotes
- Quotes
- Business Units B2B
- Shopping Lists
25
API Extensions. When you create, update, or delete API Extensions, it may take up to a minute for the changes to take effect. For more information, see Eventual Consistency.Representations
Extension
id ​String​ | Unique identifier of the Extension. |
version ​Int​ | Current version of the Extension. |
key ​String​ | User-defined unique identifier of the Extension. MinLength:Â2 ​MaxLength: 256 ​Pattern: ^[A-Za-z0-9_-]+$ ​ |
destination ​ | The configuration for the Extension, including its type, location and authentication details. |
triggers ​Array of ExtensionTrigger​ | Describes what triggers the Extension. |
timeoutInMs ​Int​ | Maximum time (in milliseconds) that the Extension can respond within.
If no timeout is provided, the default value is used for all types of Extensions, including Default:Â payment Extensions.
The maximum value is 10000 ms (10 seconds) for payment Extensions and 2000 ms (2 seconds) for all other Extensions.2000 ​ |
createdAt ​DateTime​ | Date and time (UTC) the Extension was initially created. |
createdBy ​BETACreatedBy​ | IDs and references that created the Extension. |
lastModifiedAt ​DateTime​ | Date and time (UTC) the Extension was last updated. |
lastModifiedBy ​BETA | IDs and references that last modified the Extension. |
ExtensionDraft
key ​String​ | User-defined unique identifier for the Extension. MinLength:Â2 ​MaxLength: 256 ​Pattern: ^[A-Za-z0-9_-]+$ ​ |
destination ​ | Defines where the Extension can be reached. |
triggers ​Array of ExtensionTrigger​ | Describes what triggers the Extension. |
timeoutInMs ​Int​ | Maximum time (in milliseconds) the Extension can respond within.
If no timeout is provided, the default value is used for all types of Extensions, including
payment Extensions.
The maximum value is 10000 ms (10 seconds) for payment Extensions and 2000 ms (2 seconds) for all other Extensions.This limit can be increased per Project after we review the performance impact.
Please contact the Composable Commerce support team and provide the Region, Project key, and use case. Default: 2000 ​ |
ExtensionPagedQueryResponse
limit ​Int​ | Number of results requested. Default: 20 ​Minimum: 0 ​Maximum: 500 ​ |
offset ​Int​ | Number of elements skipped. Default: 0 ​Maximum: 10000 ​ |
count ​Int​ | Actual number of results returned. |
total ​Int​ | 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 parameter withTotal=false .
When the results are filtered with a Query Predicate, total is subject to a limit. |
results ​Array of Extension​ | Extensions matching the query. |
ExtensionResourceTypeId
Extensions are available for:
cart
- Extension triggered for operations on Carts.
order
- Extension triggered for operations on Orders.
payment
- Extension triggered for operations on Payments.
customer
- Extension triggered for operations on Customers.
customer-group
- Extension triggered for operations on CustomerGroups.
quote-request
- Extension triggered for operations on QuoteRequests.
staged-quote
- Extension triggered for operations on StagedQuotes.
quote
- Extension triggered for operations on Quotes.
business-unit
- Extension triggered for operations on BusinessUnits.
shopping-list
- Extension triggered for operations on ShoppingLists.
ExtensionAction
An Extension gets called during any of the following requests of an API call, but before the result is persisted.
Create
An Extension gets called during a Create request.
Update
An Extension gets called during an Update request.
ExtensionDestination
type
, location and authentication details.HTTP
destination using AzureFunctionsAuthentication. For Google Cloud Functions, although the HTTP
destination can be used, we recommend using the dedicated GoogleCloudFunctionDestination
because invocations can be authorized through the Google Cloud Platform (GCP) Identity and Access Management (IAM). This is simpler than using the HTTPDestination
and having to provide an authorization token.
Secure access to your extension API is also possible via a GCP bridge project. See our private connectivity guide for details.Secrets or access keys are partially hidden for security when retrieving a destination.
HTTPDestination
HTTPS
connection for production setups. However, we also accept unencrypted HTTP
connections for development purposes. HTTP redirects will not be followed and cache headers will be ignored.type ​String​ | "HTTP" |
url ​String​ | URL to the target destination. |
authentication ​ | Authentication methods (such as Basic or Bearer ). |
GoogleCloudFunctionDestination
extensions@commercetools-platform.iam.gserviceaccount.com
service account to invoke your function. If your function's version is 1st gen, grant the service account the IAM role Cloud Functions Invoker
. For version 2nd gen, assign the IAM role Cloud Run Invoker
using the Cloud Run console.type ​String​ | "GoogleCloudFunction" |
url ​String​ | URL to the target function. |
To configure VPC Service Controls, specify the commercetools GCP project number as the source of your ingress policy rule. Contact your Customer Success Manager to obtain the required commercetools GCP project number.
AWSLambdaDestination
Do not use AWS Lambda if you anticipate that your API Extension will receive a JSON input exceeding 6 MB.
accessKey
and accessSecret
pair, specifically for each Extension that only has the lambda:InvokeFunction
permission on this function.type ​String​ | "AWSLambda" |
arn ​String​ | Amazon Resource Name (ARN) of the Lambda function in the format arn:aws:lambda:<region>:<accountid>:function:<functionName> . Use the format arn:aws:lambda:<region>:<accountid>:function:<functionName>:<functionAlias/version> to point to a specific version of the function. |
accessKey ​String​ | Partially hidden on retrieval for security reasons. |
accessSecret ​String​ | Partially hidden on retrieval for security reasons. |
HTTPDestinationAuthentication
Authorization
header using AuthorizationHeaderAuthentication. For calling Azure functions specifically, use AzureFunctionsAuthentication to specify the function key.AzureFunctionsAuthentication
authLevel
to function
and provide the function's key to be used inside the x-functions-key
header. For more information, see the Azure Functions documentation.https://foo.azurewebsites.net/api/bar
instead of
https://foo.azurewebsites.net/api/bar?code=secret
.key ​String​ | Partially hidden on retrieval for security reasons. |
type ​String​ | "AzureFunctions" |
ExtensionTrigger
resourceTypeId ​ | The resource that triggers the Extension. |
actions ​Array of ExtensionAction​ | Create and Update requests are supported. |
condition ​String​ | Valid predicate that controls the conditions under which the API Extension is called. The Extension is not triggered when the specified condition is not fulfilled. |
Get Extension
Get Extension by ID
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
id String ​ | id of the Extension. |
application/json
curl --get https://api.{region}.commercetools.com/{projectKey}/extensions/{id} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 1,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2017-01-25T14:14:22.417Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-extension"
}
Get Extension by Key
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
key String ​ | key of the Extension. |
application/json
curl --get https://api.{region}.commercetools.com/{projectKey}/extensions/key={key} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 1,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2017-01-25T14:14:22.417Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-extension"
}
Query Extensions
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
where | The parameter can be passed multiple times. |
sort | The parameter can be passed multiple times. |
limit Int ​ | Number of results requested. Default: 20 ​Minimum: 0 ​Maximum: 500 ​ |
offset Int ​ | Number of elements skipped. Default: 0 ​Maximum: 10000 ​ |
withTotal Boolean ​ | Controls the calculation of the total number of query results. Set to false to improve query performance when the total is not needed.Default: true ​ |
var.<varName> String ​ | Predicate parameter values. The parameter can be passed multiple times. |
application/json
curl --get https://api.{region}.commercetools.com/{projectKey}/extensions -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
{
"limit": 20,
"offset": 0,
"count": 1,
"total": 1,
"results": [
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 1,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2017-01-25T14:14:22.417Z",
"destination": {
"type": "HTTP",
"url": "https://example.org/extension"
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create"]
}
]
}
]
}
Check if Extension exists
Check if Extension exists by ID
id
. Returns a 200 OK
status if the Extension exists or a 404 Not Found
otherwise.manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
id String ​ | id of the Extension. |
curl --head https://api.{region}.commercetools.com/{projectKey}/extensions/{id} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
Check if Extension exists by Key
key
. Returns a 200 OK
status if the Extension exists or a 404 Not Found
otherwise.manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
key String ​ | key of the Extension. |
curl --head https://api.{region}.commercetools.com/{projectKey}/extensions/key={key} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
Check if Extension exists by Query Predicate
200 OK
status if any Extensions match the query predicate, or a 404 Not Found
otherwise.manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
where |
curl --head https://api.{region}.commercetools.com/{projectKey}/extensions -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
Create Extension
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
application/json
application/json
curl https://api.{region}.commercetools.com/{projectKey}/extensions -i \
--header "Authorization: Bearer ${BEARER_TOKEN}" \
--header 'Content-Type: application/json' \
--data-binary @- << DATA
{
"destination" : {
"type" : "HTTP",
"url" : "https://example.azurewebsites.net/api/extension",
"authentication" : {
"type" : "AzureFunctions",
"key" : "some-azure-function-code"
}
},
"triggers" : [ {
"resourceTypeId" : "cart",
"actions" : [ "Create", "Update" ]
} ],
"key" : "my-extension"
}
DATA
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 1,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2017-01-25T14:14:22.417Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-extension"
}
Update Extension
Update Extension by ID
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
id String ​ | id of the Extension. |
application/json
version ​Int​ | Expected version of the Extension on which the changes should be applied.
If the expected version does not match the actual version, a ConcurrentModification error will be returned. |
actions ​Array of ExtensionUpdateAction​ | Update actions to be performed on the Extension. |
application/json
curl https://api.{region}.commercetools.com/{projectKey}/extensions/{id} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}" \
--header 'Content-Type: application/json' \
--data-binary @- << DATA
{
"version" : 1,
"actions" : [ {
"action" : "setKey",
"key" : "my-new-extension-key"
} ]
}
DATA
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 2,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2024-08-06T13:49:48.511Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-new-extension-key"
}
Update Extension by Key
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
key String ​ | key of the Extension. |
application/json
version ​Int​ | Expected version of the Extension on which the changes should be applied.
If the expected version does not match the actual version, a ConcurrentModification error will be returned. |
actions ​Array of ExtensionUpdateAction​ | Update actions to be performed on the Extension. |
application/json
curl https://api.{region}.commercetools.com/{projectKey}/extensions/key={key} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}" \
--header 'Content-Type: application/json' \
--data-binary @- << DATA
{
"version" : 1,
"actions" : [ {
"action" : "setKey",
"key" : "my-new-extension-key"
} ]
}
DATA
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 2,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2024-08-06T13:49:48.511Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-new-extension-key"
}
Update actions
Set Key
action ​String​ | "setKey" |
key ​String​ | Value to set. If empty, any existing value will be removed. MinLength:Â2 ​MaxLength: 256 ​Pattern: ^[A-Za-z0-9_-]+$ ​ |
{
"action": "setKey",
"key": "keyString"
}
Change Triggers
action ​String​ | "changeTriggers" |
triggers ​Array of ExtensionTrigger​ | New value to set. Must not be empty. |
{
"action": "changeTriggers",
"triggers": [
{
"resourceTypeId": "cart",
"actions": [
"Create",
"Update"
],
"condition": "field is defined and field has changed"
}
]
}
Change Destination
action ​String​ | "changeDestination" |
destination ​ | New value to set. Must not be empty. |
{
"action": "changeDestination",
"destination": {
"type": "HTTP",
"url": "URL-String",
"authentication": {
"type": "AuthorizationHeader",
"headerValue": "valueString"
}
}
}
Set TimeoutInMs
action ​String​ | "setTimeoutInMs" |
timeoutInMs ​Int​ | Value to set. If not defined, the maximum value is used.
If no timeout is provided, the default value is used for all types of Extensions, including
payment Extensions.
The maximum value is 10000 ms (10 seconds) for payment Extensions and 2000 ms (2 seconds) for all other Extensions.This limit can be increased per Project after we review the performance impact.
Please contact the Composable Commerce support team and provide the Region, Project key, and use case. Default: 2000 ​ |
Delete Extension
Delete Extension by ID
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
id String ​ | id of the Extension. |
version Int ​ | Last seen version of the resource. |
application/json
curl -X DELETE https://api.{region}.commercetools.com/{projectKey}/extensions/{id}?version={version} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 1,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2017-01-25T14:14:22.417Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-extension"
}
Delete Extension by Key
manage_extensions:{projectKey}
region String ​ | Region in which the Project is hosted. |
projectKey String ​ | key of the Project. |
key String ​ | key of the Extension. |
version Int ​ | Last seen version of the resource. |
application/json
curl -X DELETE https://api.{region}.commercetools.com/{projectKey}/extensions/key={key}?version={version} -i \
--header "Authorization: Bearer ${BEARER_TOKEN}"
{
"id": "8062243c-46fc-40b5-88a4-75e2216aef75",
"version": 1,
"createdAt": "2017-01-25T14:14:22.417Z",
"lastModifiedAt": "2017-01-25T14:14:22.417Z",
"destination": {
"type": "HTTP",
"url": "https://example.azurewebsites.net/api/extension",
"authentication": {
"type": "AzureFunctions",
"key": "****code"
}
},
"triggers": [
{
"resourceTypeId": "cart",
"actions": ["Create", "Update"]
}
],
"key": "my-extension"
}
API Extension within the flow of the API call
The API Extensions are called during the execution of a create or update request. The system first validates that the requested operation can be done, and applies the operation. For example, if an API call requests to add a Product to a Cart, the validation checks that the Product exists and is sold in the country of the Cart, and then adds it to the Cart. However, the result (for example the Cart) is not persisted yet. It is forwarded to the API Extensions, that can run their business logic. API Extension can respond on of three ways:
- If the Extension finds the result to be valid, and does not want to perform any changes, it can return a success. The system will persist the result.
- If the Extension finds that the result is not valid (for example the particular Customer may not purchase this Product), it can return a list of errors. The system will not persist the result and return the errors to the original caller of the API.
- If the Extension wants to perform additional changes, it can return a list of update actions (for example if it wants to add mandatory insurance to the Cart, it can return an
AddLineItem
update). The system will validate that the update actions are valid for the given resource type and will try to perform the updates. Should that fail, the original caller of the API will receive the errors. Otherwise, the original caller will receive the final result (for example the Cart including the mandatory insurance).
If an API Extension fails to respond properly
504
Gateway Timeout
HTTP status code. If the API Extension returns a response, but it could not be parsed properly, the original API caller will receive a 502
Bad Gateway
HTTP status code.In both cases, additional information on the cause of the failure is returned to the original API Client.
In terms of Service Level Agreement (SLA), a failure caused by an API Extension does not count as a failure of the commercetools Composable Commerce API. For example, if an API Extension for the Carts is down and causes downtime for your shop, the SLA won't cover that.
Multiple API Extensions in a single API call
If multiple API Extensions are triggered by an API call, they will be called in parallel. Their responses will be merged, but without a guaranteed order (for example, if two Extensions each return an error, their order in the error list is undefined. If two Extensions return updates, the order in which the updates are performed is undefined).
Responses are merged based on their priority or severity:
- A failure to respond properly by any API Extension will cause the whole request to fail with a
502
or504
. - Otherwise, if any API Extension finds that the result is not valid, the result will not be persisted and an error will be returned to the original caller of the API.
- Otherwise, if any API Extension returns updates, the result will be modified before it is returned to the original caller of the API.
API Extensions should operate on separate concerns. For example for a Cart, it is fine to have an Extension each for validating that a Customer is allowed to purchase age-restricted Products, calculating shipping costs, and adding mandatory insurance. A counter-example is two API Extensions changing the Price of a Line Item: One for adding extra costs for optional gift wrapping, the other reducing the Price if an externally defined Discount applies - If both Extensions want to change the same Line Item, one will overwrite the result of the other. For this use case, the whole Price calculation for Line Items should be performed inside a single Extension.
You also need to find a balance between clean separation and the increasing latency risk when calling many parallel Extensions.
Input
HTTP
Extension will be called via HTTP POST request. An AWSLambda
Extension will be invoked, and the input is provided as the payload.action ​ | Create or Update request. |
resource ​Reference​ | Expanded reference to the resource that triggered the Extension. |
lastModifiedAt
field, and if it is a creation, the createdAt
field, do not contain the final values. All other fields will be persisted if they are not overridden by an update of an Extension.Headers
HTTP
Extension, some headers will be set:Content-Type
-application/json
X-Correlation-ID
- A correlation ID can be used to track a request. The same correlation ID will be returned to the original caller of the API.
Response
HTTP
destination must set a proper HTTP status code (200
or 201
for successful responses, 400
for validation failures). All other status codes will be treated as a failure to respond properly.An Extension with an AWSLambda destination must return without errors (both for successful responses, and validation failures). Throwing an exception in Java, Python, C# or Go, or invoking the callback with an error in NodeJS, will be treated as a failure to respond properly.
100
update actions to be applied to the resource. The possible update actions are limited to those available for the resource that triggered the API Extension.Validation successful / No updates requested
HTTP
Extension must set a 200
or 201
HTTP status code.Validation failed
errors
- Array of Error - At least one error must be present.
HTTP
Extension must set a 400
HTTP status code.AWSLambda
Extension must add the following field to the JSON:responseType
- String -"FailedValidation"
Error
code
- String
Needs to be one of the defined error codes, for example,InvalidInput
orInvalidOperation
.message
- String
User-defined description of the error.localizedMessage
- LocalizedString - Optional
Localized user-defined description of the error. If a localized message is available for the user's locale settings, the Merchant Center displays it.extensionExtraInfo
- JSON Object - Optional
Any other information that should be returned to the API caller.
Updates requested
actions
- Array of update actions for the resourceType.
Up to100
update actions are allowed.
You can use all update actions for the particular resource:
- for
cart
you can use any Cart update action. - for
order
you can use any Order update action. - for
payment
you can use any Payment update action. - for
customer
you can use any Customer update action. - for
customer-group
you can use any Customer Group update action. - for
quote-request
you can use any Quote Request update action. - for
staged-quote
you can use any Staged Quote update action. - for
quote
you can use any Quote update action. - for
business-unit
you can use any Business Unit update action B2B. - for
shopping-list
you can use any Shopping List update action.
HTTP
Extension must set a 200
or 201
HTTP status code.AWSLambda
Extension must add the following field to the JSON:responseType
- String -"UpdateRequest"
Conditional triggers
By default, API Extensions are always called during an update or create action on the resource they are configured for. However, in certain use cases, the Extension should only be triggered when certain conditions are met. Specifying conditional triggers can eliminate unnecessary API calls to your Extension. This eases the load on your service as well as improves overall performance.
condition
field of the ExtensionTrigger. The condition is evaluated during every create or update action to the configured resource, resulting in one of three outcomes:- The condition evaluates to true and the API Extension is called.
- The condition evaluates to false, the API Extension is not called.
- The predicate syntax can not be evaluated and the entire API call fails.
is defined
operator to ensure that the field exists and has a non-null value. This will ensure that a missing field will not cause the evaluation of the condition to fail.Limits and error cases
An API Extension affects the performance and uptime of the API it is extending. Therefore, you should carefully consider the technology used to implement the API Extension and your hosting options.
We believe that deploying an Extension on a Function-as-a-Service is a good fit. You should get a very high up-time and auto-scaling at low costs. However, you should optimize your functions for good cold-start performance.
Time limits
If an API Extension is not responding fast, the whole API call is blocked. We, therefore, enforce the following limits per default:
- An API Extension must return a result within 2 seconds. This includes the network latency.
- For an API Extension with a
payment
trigger, the time limit can be raised to 10 seconds. - The commercetools Composable Commerce API must successfully establish a connection to the API Extension within 1 second.
We recommend that your API Extension returns results much faster, though. A good target is to respond within 50 ms.
Error cases
500
HTTP status code) the API call fails. The API Extension is not retried within an API call, but further API calls will try to reach the API Extension again.The following errors codes are returned when an API Extension does not respond successfully: