API Reference

Feed v3

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.

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.


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.


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.


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.


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.

"filter": { "type": "FromWorkflow", "status": [ “order-completed”, "ready-for-handling", “start-handling”, “handling”, “waiting-ffmt-authorization”, “cancel” ] }

The FromWorkflow filter may be limited for some of the integration needs of your store. If you want more filter options, consider using the FromOrders filter.


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.

"filter": { "type": "FromOrders", "expression": "status = \"payment-pending\"", "disableSingleFire": false }

The two filter types are mutually exclusive. If you pass FromWorkflow in the type field, you should not include the expression field nor disableSingleFire in the body. The reverse is also true. If you pass FromOrders type, you should not include the status field. If you combine them, you will get a 409 conflict status response.

If the filter is not configured, the FromWorkflow type is used, and all status changes will appear in the feed.


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
isAllDelivered = true
  • Orders with added items
$count(changesAttachment.changesData.itemsAdded) > 0
  • Orders with removed items
$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:

$count(items[name ~> /.*refrigerator.*/i and price>=1000]) > 0

Here are some additional expression examples:

  • Order in specific status and trade policy (sales channel)
(status = "ready-for-handling" and salesChannel="2")
  • Orders from a seller that don't have a specific trade policy (sales channel)
(salesChannel.Id != "3" and ="sellerId")
  • Order with refund/item return
$count(packageAttachment.packages.restitutions.Refund.value) > 0
  • Order invoiced with a specific shipping policy
status = "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 expression status = “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.

"filter": { "type": "FromOrders", "expression": "(status = \"handling\" and salesChannel = \"2\" and $count(packageAttachment.packages.restitutions.Refund.value) > 0)", “disableSingleFire”: false },
Expression tests

You should validate the events of the configured expression before implementing the filter in your integration. There are three useful tests:


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 the expression field. Because of this, if the disableSingleFire field is set to true, orders may appear more than once in a feed — even hundreds of times in some cases. To prevent that from happening, keep disableSingleFire set to false.


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:

"queue": { "visibilityTimeoutInSeconds": 240, "messageRetentionPeriodInSeconds": 345600 }
  • 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).


Here are two complete example bodies for the Feed configuration response, using each filter type:

{ "filter": { "type": "FromWorkflow", "status": [ “order-completed”, "ready-for-handling", “start-handling”, “handling”, “waiting-ffmt-authorization”, “cancel” ] }, "queue": { "visibilityTimeoutInSeconds": 240, "messageRetentionPeriodInSeconds": 345600 }, "quantity": 1261, "aproximateAgeOfOldestMessageInSeconds": 1113.349305555555 }
{ "filter": { "type": "FromOrders", "expression": "status = \"payment-pending\"", "disableSingleFire": false }, "queue": { "visibilityTimeoutInSeconds": 240, "messageRetentionPeriodInSeconds": 345600 }, "quantity": 1261, "aproximateAgeOfOldestMessageInSeconds": 1113.349305555555 }

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.


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:

  1. It retrieves ten events from the feed.
  2. For each one of the ten events, it evaluates the status the order was changed to.
  3. 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 to Handling shipping (which indicates the start of handling).
  4. Then, it commits the events to the Feed (removing them from the queue).
  5. 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.

{"base64":"  ","img":{"width":250,"height":472,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":37671,"url":""}}

Check the back-office order integration guide to learn more about how you can integrate the VTEX order feed with an ERP.


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.


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:


{ "filter": { "type": "FromWorkflow", "status": [ "payment-pending" ] }, "hook": { "url": "https://endpoint.example/path", "headers": { "key": "value" } } }


{ "filter": { "type": "FromOrders", "expression": "status = \"payment-pending\"", "disableSingleFire": true }, "hook": { "url": "https://endpoint.example/path", "headers": { "key": "value" } } }

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:

{ “hookConfig”: “ping” }

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.

{ "Domain": "Marketplace", "OrderId": "v40484048naf-01", "State": "payment-approved", "LastChange": "2019-07-29T23:17:30.0617185Z", "Origin": { "Account": "accountABC", "Key": "vtexappkey-keyEDF" } }

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.

Photo of the contributor
+ 1 contributors
Was this helpful?
Suggest edits (Github)
Photo of the contributor
+ 1 contributors
On this page