Query parameters
All optional. Needs Products and changes: Read.
| Parameter | Type | Meaning |
|---|---|---|
| cursor | integer | The meta.next_cursor of the answer before, or the meta.changes_cursor of GET /products. Without it the answer has no changes, and meta.next_cursor is where to start from now. |
| limit | integer | Changes per answer, 1 to 500. Default 100. |
- Read your products with GET /products and keep the
meta.changes_cursorof its first page. - Call
GET /changes?cursor=with it. Apply each change, then call again withmeta.next_cursor. Every answer has one, even when nothing changed, and it moves on past changes that are not for your key, so save it each time. - When
meta.has_moreistrue, call again straight away; otherwise, a few minutes later.
Each change
Exactly what a webhook delivery carries. Apply each once, by event_id; reading one twice is harmless.
| Field | Meaning |
|---|---|
| event_id | The change's identity: the same change read twice has the same event_id. |
| event_type | What kind of change, such as merchant_price.recomputed. See the table below. Ignore a type you do not know. |
| event_version | The version of the payload's shape. |
| license_id | Your store's licence, or null for a change to the shared catalog (product.*). |
| aggregate_type | What kind of thing changed, such as product. |
| aggregate_id | Which one. |
| cursor | The change's position. It only grows. |
| occurred_at | When it happened. |
| payload | The state after the change, never a difference: apply it as it is. |
| metadata | Where the change came from. For information, not for deciding whether to apply it. |
The changes your key gets
| Permission | event_type | What to do |
|---|---|---|
| Products and changes | merchant_price.* | merchant_price.recomputed: your price, stock or availability moved. payload.products holds each product's new price, stock, sourced_from and is_visible; apply them as they are, and take a product with is_visible false off sale. |
| Products and changes | product.* | product.created and product.updated: a product in the shared catalog changed, its title, images or category, say. These are about the whole catalog, not only your products: fetch the product with GET /products?ids=, and one you have no price for comes back empty. |
| Pricing rules | pricing_rule.* | pricing_rule.created, pricing_rule.updated and pricing_rule.deleted: a rule was saved or removed, whoever did it; pricing_rule.reordered: the order changed, and payload.ordered_ids is the new one. A payload names categories, brands and product classes by internal ids, not slugs: read the rule with GET /pricing-rules/{id} (payload.pricing_rule_id), unless it was deleted. Your prices follow as merchant_price.recomputed. Only for a key that also has Pricing rules. |
| Pricing rules | pricing_settings.* | pricing_settings.updated: a store-wide pricing setting changed, on your Pricing screen or with PATCH /pricing-settings. Its payload holds all three, default_margin, price_rounding and enforce_map_floor, as GET /pricing-settings answers them. Your prices follow as merchant_price.recomputed. Only for a key that also has Pricing rules. |
| Restrictions | restriction.* | restriction.created, restriction.updated and restriction.deleted: a restriction was saved or removed, whoever did it. Its payload names catalog rows by internal ids too: read it with GET /restrictions/{id} (payload.restriction_id), unless it was deleted. What it hides follows as merchant_price.recomputed. Only for a key that also has Restrictions. |
| Distributors | distributor_account.*, license.distributor_selection.* | distributor_account.connected: credentials for a distributor were saved, or replaced, whoever did it; distributor_account.disconnected: they were deleted. payload.distributor_slug names the distributor, and no payload carries a credential. license.distributor_selection.changed: the distributors you buy from changed, and payload.selected_distributors is the new list. Your prices follow as merchant_price.recomputed. Only for a key that also has Distributors. |
| In-house inventory | merchant_inventory.* | merchant_inventory.updated: a row of your in-house inventory was written or removed. Only for a key that also has In-house inventory. |
| Subscription | license.activated, license.suspended, license.canceled | Your licence started, stopped or ended serving: payload.status is the new status. Read GET /subscription for the detail. Only for a key that also has Subscription. |
| Subscription | subscription.* | subscription.ending_soon: your subscription is set to end within seven days. payload.ends_at is when. Call GET /subscription for the link to renew; the change carries none. |
You never get another store's changes. If your store switches catalog sync off, product, price and in-house inventory changes are left out while it is off, and are not replayed later, and meta.sync_paused is true. Once it is false again, read your products again. To have changes sent to you instead of asking for them, set a webhook.
What it answers
| Field | Meaning |
|---|---|
| next_cursor | Where to read from next. On every answer, and it moves on even when no change was for your key. |
| has_more | true when the answer is full: read again from next_cursor straight away. |
| sync_paused | true while your store has switched catalog sync off, so product, price and in-house inventory changes were left out of this answer. They are not replayed: when it turns false, read GET /products again. |
Returns
- 200The changes after the cursor, oldest first, in
data, andmeta. - 410cursor_expired: changes are kept for at least 30 days, and this cursor is older than what is kept. Read GET /products again and carry on from its
meta.changes_cursor. - 422
validation_failed: a query parameter broke a rule.
This endpoint can also answer 401 invalid_api_key, 403 license_not_active, 403 product_not_included, 403 insufficient_scope and 429 rate_limited.