The order feed is a list of order updates. This means that whenever an order is updated, the event is included as a new entry in the feed. Updates can include status changes, items added or removed by the store, order delivered, and others.
In this sense, the feed is not a list of orders, but rather a list of events. For example, if the status of an order is changed to Approve payment
and then to Authorize shipping
, the feed will receive two events: one for each update, both related to the same order. You can configure the feed to filter the updates that will actually generate feed events, instead of having all updates in all orders generating events in the feed queue.
This guide explains how Feed and Hook work and how to configure them to build order integrations. Also, in the latter part of the article, we explain the differences between each and when to choose one over the other based on the specific needs of your operation.
Feed and Hook are independent configurations to build order integrations.
Best practices for integrations
When designing an orders integration, consider the practices below to increase performance and avoid throttling errors:
- Optimize your code to get only the required data.
- Use caching for often-used data.
- Consider including code that catches errors. By ignoring these errors and persisting in making requests, your app will not be able to recover gracefully.
- After getting a 429 status code error, you should stop making additional API requests and wait before retrying. We recommend a 1 minute backoff time.
- Configure your integration to communicate asynchronously with VTEX APIs in order to keep requests in a queue and do other processing tasks while waiting for the next queued job to run.
If your integration is getting 429 status code errors, we recommend you to regulate the rate of your requests for smoother distribution. When the account exceeds the request limit, VTEX Admin might also become unavailable.
Order integration: Feed vs. List orders
Some stores use the List orders API request to check order status changes. However, this method only returns orders that have already been indexed, which may lead to some problems:
-
If a problem in the system prevents indexing, you will not be able to consume the order list, and order updates may not be visible.
-
Because the
list
method depends on indexing, it's slower and performs less than the feed.
On the other hand, the feed has been specifically developed to track order updates. It runs before indexing and doesn't depend on it, making it the most reliable and fastest method to track order updates.
If you have an integration based on the List orders API request, you should migrate it to the feed. However, keep in mind that this means changing the integration flow. Read the order integration guide to learn how to implement this change.
Access
Configuring and using Feed v3 and Hook is only allowed when authorization is granted in the Order Management module.
The feed's application key must have a role with one of the appropriate resources: Feed v3 and Hook Admin
or Feed v3 and Hook view only
, depending on the intended use.
Each appKey can configure or access only one feed. This means that different users sharing an appKey access the same feed. In this case, if a user commits an item to the queue, the item is removed from the feed and won't be available for any users sharing the same appKey. Therefore, we recommend configuring one feed per appKey per user, ensuring that each user has access to their own feed.
Configuration
The feed is set up through a POST request to the Create or update Feed configuration endpoint.
The body of this request has two objects, the filter
and the queue
.
filter
When you configure the filter, you define which order updates should be displayed in the feed. Two types of filters can be used in the feed: FromWorkflow
and FromOrders
. You can see an example and description of each filter below.
FromWorkflow
When you use the FromWorkflow
configuration, you can only filter order updates by status. You can pass an array of statuses in the field status
, and whenever an order status is changed to one of those statuses, an update will appear in the feed.
You can see the list of possible order statuses in the article Order flow in orders management.
_11"filter": {_11 "type": "FromWorkflow",_11 "status": [_11 “order-completed”,_11 "ready-for-handling",_11 “start-handling”,_11 “handling”,_11 “waiting-ffmt-authorization”,_11 “cancel”_11 ]_11 }
The
FromWorkflow
filter may be limited for some of the integration needs of your store. If you want more filter options, consider using theFromOrders
filter.
FromOrders
FromOrders
allows you to filter updates in your feed by any property in the order JSON document, not only status changes. This is done with a JSONata expression passed in the expression
field.
_10"filter": {_10 "type": "FromOrders",_10 "expression": "status = \"payment-pending\"",_10 "disableSingleFire": false_10 }
The two filter types are mutually exclusive. If you pass
FromWorkflow
in thetype
field, you should not include theexpression
field nordisableSingleFire
in the body. The reverse is also true. If you passFromOrders
type
, you should not include thestatus
field. If you combine them, you will get a409 conflict
status response.
If the
filter
is not configured, theFromWorkflow
type is used, and all status changes will appear in the feed.
expression
This field receives a string with a JSONata query expression that defines what conditions must be met for an order to be included in the feed.
JSONata is a query and transformation language for JSON data. You can learn more about it in the JSONata documentation.
This filter offers many possibilities that can't be achieved with the FromWorkflow
configuration. For example, you can filter orders that have been delivered or had items added or removed by the store. You can also combine two or more selection criteria. See the examples below:
- Delivered orders
_10isAllDelivered = true
- Orders with added items
_10$count(changesAttachment.changesData.itemsAdded) > 0
- Orders with removed items
_10$count(changesAttachment.changesData.itemsRemoved) > 0
You can also filter multiple properties at the same time. For example, the following is an expression that filters orders that contain at least one refrigerator that costs at least $1000.00:
_10$count(items[name ~> /.*refrigerator.*/i and price>=1000]) > 0
Here are some additional expression examples:
- Order in specific status and trade policy (sales channel)
_10(status = "ready-for-handling" and salesChannel="2")
- Orders from a seller that don't have a specific trade policy (sales channel)
_10(salesChannel.Id != "3" and sellers.id ="sellerId")
- Order with refund/item return
_10$count(packageAttachment.packages.restitutions.Refund.value) > 0
- Order invoiced with a specific shipping policy
_10status = "invoiced" and (packageAttachment.packages[$[$contains($string(courier), "Carrier Name")]])
Keep in mind that the
expression
field receives only strings and all JSONata expressions have to be escaped. For example, the expressionstatus = “canceled”
has to be passed as”status = \\”canceled\\””
to be read correctly by the API.
The following is an example of a complete filter
object with a more complex and escaped JSONata expression.
_10"filter": {_10 "type": "FromOrders",_10 "expression": "(status = \"handling\" and salesChannel = \"2\" and $count(packageAttachment.packages.restitutions.Refund.value) > 0)",_10 “disableSingleFire”: false_10},
Expression tests
You should validate the events of the configured expression before implementing the filter in your integration. There are three useful tests:
- The API endpoint for JSONata Testing.
- JSONata Exerciser which tests the expression against a real JSON file. To run this test, copy the order to JSONata Exerciser and simulate different expressions and requests. Use an order extracted from the Get Order API endpoint.
- Configure a test feed or hook with a test appKey and check whether the events behave as expected.
disableSingleFire
This field limits how often a specific order shows in the feed after it meets the filter conditions. If this field is false
, orders will appear in the feed only once.
The
FromOrders
filter receives order updates whenever any change is made to the order JSON document, provided the order meets the criteria set in theexpression
field. Because of this, if thedisableSingleFire
field is set totrue
, orders may appear more than once in a feed — even hundreds of times in some cases. To prevent that from happening, keepdisableSingleFire
set tofalse
.
queue
The properties of this object define the behavior of feed items once they are included in the feed or are retrieved. This is an example of the queue
object:
_10"queue": {_10 "visibilityTimeoutInSeconds": 240,_10 "messageRetentionPeriodInSeconds": 345600_10 }
-
visibilityTimeoutInSeconds
- This is the maximum time after retrieving an item from the feed when it can be committed. When a user retrieves the events from the feed queue using the Retrieve Feed items API request, the returned items are omitted from the feed for the time set in this field. Then, the user may take any necessary actions and commit the items to the feed. If events are not committed, they are returned to the feed after this time expires. -
MessageRetentionPeriodInSeconds
- Items will be excluded from the feed — even if they aren't committed — when they stay in the feed longer than the retention period defined in this field.
Other fields
There are also a couple of other fields that give us some information about the state of the field:
quantity
: Current number of messages in the feed, including messages that may not be visible due to time out after retrieval.approximateAgeOfOldestMessageInSeconds
: Approximate age of the oldest message in the feed (in seconds).
Examples
Here are two complete example bodies for the Feed configuration response, using each filter
type:
_19{_19 "filter": {_19 "type": "FromWorkflow",_19 "status": [_19 “order-completed”,_19 "ready-for-handling",_19 “start-handling”,_19 “handling”,_19 “waiting-ffmt-authorization”,_19 “cancel”_19 ]_19 },_19 "queue": {_19 "visibilityTimeoutInSeconds": 240,_19 "messageRetentionPeriodInSeconds": 345600_19 },_19 "quantity": 1261,_19 "aproximateAgeOfOldestMessageInSeconds": 1113.349305555555_19}
_13{_13 "filter": {_13 "type": "FromOrders",_13 "expression": "status = \"payment-pending\"",_13 "disableSingleFire": false_13 },_13 "queue": {_13 "visibilityTimeoutInSeconds": 240,_13 "messageRetentionPeriodInSeconds": 345600_13 },_13 "quantity": 1261,_13 "aproximateAgeOfOldestMessageInSeconds": 1113.349305555555_13}
When a new feed is configured, its queue contains whatever orders are changed right after the setup is complete. If the feed is reconfigured, events from the former queue will remain in the feed until they are committed or until the retention period expires.
If the feed doesn't receive any new events in its queue during the time set in
messageRetentionPeriodInSeconds
, your configuration will be removed, and you will have to reconfigure it with the Feed configuration API call to continue using the feed. Therefore, it's important to be mindful of the filter configuration you are using. You can check it any time using the Get feed configuration endpoint.
Feed readout
Every system that depends on order updates should consume the feed to be able to take the necessary actions based on that information. The most common behavior is a store system or an integration reading every event in the feed and, based on its status, making a decision for each one.
When filtering statuses, be aware that all possible order statuses must be dealt with during integration to avoid errors. Particular attention should be paid to
Status Null
, which may be unidentified and end up being mapped as another status, which can potentially lead to errors.
Example
The order feed can be useful in many ways. For instance, say you want to develop an integration between your ERP and VTEX. This integration could have the following behavior:
- It retrieves ten events from the feed.
- For each one of the ten events, it evaluates the status the order was changed to.
- If the order changed to the status
Ready for handling
, the integration gets the complete order data, records it in the ERP, and updates the status on VTEX toHandling shipping
(which indicates the start of handling). - Then, it commits the events to the Feed (removing them from the queue).
- After reading and removing these ten events from the list, the integration moves to the following ten events in the feed and repeats the process.
Check the back-office order integration guide to learn more about how you can integrate the VTEX order feed with an ERP.
Hook
Hook is a counterpart to Feed. It allows integrations to consume order update data differently. Instead of receiving events to form a queue that can be retrieved, a hook automatically sends the items to a URL provided by the user in the hook configuration.
Since Hook is a counterpart, access follows the same principles described for Feed above. This means each appKey can configure or access only one hook. Different users that sharing the same appKey access the same hook. We recommend configuring one hook per appKey per user, ensuring that each user has access to their own hook.
Configuration
Similar to a feed, a hook can be configured through a POST call to the Create or update hook configuration endpoint of the Orders API. Here are a couple of body examples for the request, each using a different filter type:
FromWorkflow
_14{_14 "filter": {_14 "type": "FromWorkflow",_14 "status": [_14 "payment-pending"_14 ]_14 },_14 "hook": {_14 "url": "https://endpoint.example/path",_14 "headers": {_14 "key": "value"_14 }_14 }_14}
FromOrders
_13{_13 "filter": {_13 "type": "FromOrders",_13 "expression": "status = \"payment-pending\"",_13 "disableSingleFire": true_13 },_13 "hook": {_13 "url": "https://endpoint.example/path",_13 "headers": {_13 "key": "value"_13 }_13 }_13}
As you can see, the filter
object is configured the same way as the filter
in the feed configuration described above. The other object in the body is the hook
object, which contains information about how the data should be sent to the integration:
-
url
is the endpoint path that should receive the order update information. -
headers
contains the credentials that should be used to access the given endpoint. -
key
is the endpoint key that should be used to access the given endpoint.
When the hook is configured, VTEX sends a ping to the endpoint given in the configuration request body to ensure it is up and running. It is a POST
request similar to this:
_10{_10 “hookConfig”: “ping”_10}
The given endpoint should return status 200 for the above-mentioned request. Otherwise, the Hook API will return status
400 Bad Request
, and you won't be able to save the configuration.
We recommend configuring the hook in the main account. This ensures more visibility to commit all events correctly and that the configured endpoint is more frequently notified, preventing the hook from being excluded due to inactivity.
Hook notifications
If configured, the Hook notifies the integration endpoint whenever an order update is made and meets the conditions specified in the filter
.
If a new event is not correctly notified to the endpoint, the interval for future retries is recalculated based on an internal geometric progression algorithm.
If the hook has no notifications for three days, your configuration will be removed, and you will have to reconfigure it with the Hook configuration API call to continue using it. Therefore, it's important to be mindful of your filter configuration. You can check it any time using the Get hook configuration endpoint.
When notified, the configured endpoint must always respond with HTTP status 200 within 5000 ms. Below is an example of a hook notification request body made to the integration endpoint.
_10{_10 "Domain": "Marketplace",_10 "OrderId": "v40484048naf-01",_10 "State": "payment-approved",_10 "LastChange": "2019-07-29T23:17:30.0617185Z",_10 "Origin": {_10 "Account": "accountABC",_10 "Key": "vtexappkey-keyEDF"_10 }_10}
When using a hook, it's important to be aware that it's a reactive feature. This means your middleware or ERP system must be ready to deal with whatever volume of data the hook sends. Large peaks in sales – due to Black Friday, for example – tend to increase hook notifications. If the implementation is not prepared for this peak, it may cause problems in the integration, compromising the store’s ability to handle orders and receive further notifications. Learn more about how to deal with this issue in the next section. Getting order updates from a feed requires the integration to make periodical API calls, returning whatever number of updates is available each time. A hook, on the other hand, notifies the integration whenever a new update is available. This means a feed is active, whereas a hook is reactive.
Because of this, a hook can be more efficient and provide a lower response time for each order update. But as a reactive feature, the integration must have scalability to handle great variations in data volume, such as can be caused by a Black Friday sales peak, for example.
Some ERPs, for instance, do not have this scaling capacity. If you integrate a hook with an ERP, a possible workaround is to build middleware that is able to handle large variations in the data volume received from the hook and have the integration send the data to the ERP at a lower speed based on the capacity of the ERP.
Another option is using a hook as the primary source of data for the integration and a feed as a backup source that may be used when the hook integration has trouble scaling.
We recommend a hook for larger and more complex operations, which tend to have greater middleware scalability capacity and can benefit more from the feature’s efficiency.
On the other hand, a feed requires active retrieving and committing items to the queue. This means the integration has control over how many order updates it receives, and it knows how much data it needs to be able to handle at any given time. For this reason, it is less likely for the integration to crash or miss updates from the queue.
To learn more, read the order integration with ERP guide and the API reference for Feed v3 and Hook.