|
| 1 | +--- |
| 2 | +title: "Real-time catalog updates" |
| 3 | +description: "Using webhooks to get real-time updates on product catalog changes" |
| 4 | +icon: "webhook" |
| 5 | +--- |
| 6 | + |
| 7 | +Instead of polling the product catalog for changes, you can use the `product.update` webhook to get real-time update pushed to your system when a product is added, updated or removed from your catalog. |
| 8 | + |
| 9 | +## How to use the `product.update` webhook |
| 10 | + |
| 11 | +An event is triggered when there is an update to a product in the catalog. If multiple products are updated at once, an event is triggered for each product. This can be a product being added, updated or removed from the catalog. |
| 12 | + |
| 13 | +Attempts to order a product that is no longer available or for a denomination that is no longer available will be rejected. If you subscribe to the webhook, you will receive an event for each product that is updated and can take action to avoid these rejections. |
| 14 | + |
| 15 | +We won't cover the generic details of how to set up a webhook subscription, but you can read about that in the [webhook reference](/reference/webhooks). |
| 16 | + |
| 17 | +<CardGroup cols={2}> |
| 18 | + <Card |
| 19 | + title="Webhook reference" |
| 20 | + icon="webhook" |
| 21 | + href="/reference/webhooks" |
| 22 | + horizontal |
| 23 | + arrow |
| 24 | + > |
| 25 | + Read about how webhooks work in the Runa API and how to set up a |
| 26 | + subscription in your system. |
| 27 | + </Card> |
| 28 | + <Card |
| 29 | + title="Product update webhook reference" |
| 30 | + icon="money-check-pen" |
| 31 | + href="/reference/2024-02-05/webhook/product.update" |
| 32 | + horizontal |
| 33 | + arrow |
| 34 | + > |
| 35 | + The specific details of the `product.update` webhook event |
| 36 | + </Card> |
| 37 | +</CardGroup> |
| 38 | + |
| 39 | +### Order of events |
| 40 | + |
| 41 | +<Warning> |
| 42 | + Due to the nature of webhooks you **cannot rely on the order of the events**. |
| 43 | +</Warning> |
| 44 | + |
| 45 | +If two updates happen in quick succession, you may receive the events in the wrong order. For example: |
| 46 | + |
| 47 | +- An event for `is_orderable: false` followed by an event for `is_orderable: true`. |
| 48 | +- When the product was actually made non-orderable, `is_orderable: false`. |
| 49 | +- The events arrived in the wrong order. |
| 50 | + |
| 51 | +Each event has a `timestamp` attached. You should store the `timestamp` and discard any events that are older than the last event you received. This will ensure that you always have the most up-to-date information about the product. |
| 52 | + |
| 53 | +### Product Availability `is_orderable` |
| 54 | + |
| 55 | +#### When a product becomes non-orderable |
| 56 | + |
| 57 | +- _Action:_ Disable the product in your catalog or mark as "Unavailable" |
| 58 | +- _Reason:_ Orders for this product will be rejected by the ordering endpoint |
| 59 | + |
| 60 | +```json Example payload where a product becomes non-orderable {5,15} [expandable] |
| 61 | +{ |
| 62 | + "product_code": "XYZ-US", |
| 63 | + "old_state": { |
| 64 | + "price_multiplier": "0.0098", |
| 65 | + "is_orderable": true, |
| 66 | + "denominations": { |
| 67 | + "type": "fixed", |
| 68 | + "available_list": ["10", "25", "50"], |
| 69 | + "minimum_value": "10.0", |
| 70 | + "maximum_value": "50.0" |
| 71 | + } |
| 72 | + }, |
| 73 | + "new_state": { |
| 74 | + "price_multiplier": "0.0098", |
| 75 | + "is_orderable": false, |
| 76 | + "denominations": { |
| 77 | + "type": "fixed", |
| 78 | + "available_list": ["10", "25", "50"], |
| 79 | + "minimum_value": "10.0", |
| 80 | + "maximum_value": "50.0" |
| 81 | + } |
| 82 | + }, |
| 83 | + "timestamp": "2025-08-26T18:39:27.006833+00:00" |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +#### When a product becomes orderable again |
| 88 | + |
| 89 | +- _Action:_ Enable the product in your catalog |
| 90 | +- _Reason:_ Orders for this product can now be successfully processed |
| 91 | + |
| 92 | +```json Example payload where a product becomes orderable again {5,15} [expandable] |
| 93 | +{ |
| 94 | + "product_code": "XYZ-US", |
| 95 | + "old_state": { |
| 96 | + "price_multiplier": "0.0098", |
| 97 | + "is_orderable": false, |
| 98 | + "denominations": { |
| 99 | + "type": "fixed", |
| 100 | + "available_list": ["10", "25", "50"], |
| 101 | + "minimum_value": "10.0", |
| 102 | + "maximum_value": "50.0" |
| 103 | + } |
| 104 | + }, |
| 105 | + "new_state": { |
| 106 | + "price_multiplier": "0.0098", |
| 107 | + "is_orderable": true, |
| 108 | + "denominations": { |
| 109 | + "type": "fixed", |
| 110 | + "available_list": ["10", "25", "50"], |
| 111 | + "minimum_value": "10.0", |
| 112 | + "maximum_value": "50.0" |
| 113 | + } |
| 114 | + }, |
| 115 | + "timestamp": "2025-08-26T18:39:27.006833+00:00" |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +### Pricing Changes `price_multiplier` |
| 120 | + |
| 121 | +#### When the price multiplier changes |
| 122 | + |
| 123 | +- _Action:_ Update the product pricing and discounts in your catalog |
| 124 | +- _Reason:_ The product's discount has changed |
| 125 | + |
| 126 | +```json Example payload where the price multiplier changes {4,14} [expandable] |
| 127 | +{ |
| 128 | + "product_code": "STARBUCKS-US", |
| 129 | + "old_state": { |
| 130 | + "price_multiplier": "0.0095", |
| 131 | + "is_orderable": true, |
| 132 | + "denominations": { |
| 133 | + "type": "fixed", |
| 134 | + "available_list": ["10", "25", "50"], |
| 135 | + "minimum_value": "10.0", |
| 136 | + "maximum_value": "50.0" |
| 137 | + } |
| 138 | + }, |
| 139 | + "new_state": { |
| 140 | + "price_multiplier": "0.0090", |
| 141 | + "is_orderable": true, |
| 142 | + "denominations": { |
| 143 | + "type": "fixed", |
| 144 | + "available_list": ["10", "25", "50"], |
| 145 | + "minimum_value": "10.0", |
| 146 | + "maximum_value": "50.0" |
| 147 | + } |
| 148 | + }, |
| 149 | + "timestamp": "2025-08-26T18:39:27.006833+00:00" |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +### Denomination Updates `denominations` |
| 154 | + |
| 155 | +The `denominations` object represents available values you can order for a product. There are two types: |
| 156 | + |
| 157 | +- `fixed`: Predetermined amounts like \$10, \$25, \$50 |
| 158 | +- `open`: Any amount within a specified range |
| 159 | + |
| 160 | +#### When `fixed` denominations change |
| 161 | + |
| 162 | +- _Action:_ Update the product denominations in your catalog |
| 163 | +- _Reason:_ The product denominations have changed, orders for the removed denominations will be rejected |
| 164 | + |
| 165 | +```json Example payload where a denomination is removed {4,14} [expandable] |
| 166 | +{ |
| 167 | + "product_code": "XYZ-US", |
| 168 | + "old_state": { |
| 169 | + "price_multiplier": "0.0104", |
| 170 | + "is_orderable": true, |
| 171 | + "denominations": { |
| 172 | + "type": "fixed", |
| 173 | + "available_list": ["15", "25", "50", "100"], |
| 174 | + "minimum_value": "15.0", |
| 175 | + "maximum_value": "100.0" |
| 176 | + } |
| 177 | + }, |
| 178 | + "new_state": { |
| 179 | + "price_multiplier": "0.0104", |
| 180 | + "is_orderable": true, |
| 181 | + "denominations": { |
| 182 | + "type": "fixed", |
| 183 | + "available_list": ["25", "50", "100"], |
| 184 | + "minimum_value": "25.0", |
| 185 | + "maximum_value": "100.0" |
| 186 | + } |
| 187 | + }, |
| 188 | + "timestamp": "2025-08-26T18:39:27.006833+00:00" |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +#### When `open` denominations change |
| 193 | + |
| 194 | +- _Action:_ Update the product denominations in your catalog |
| 195 | +- _Reason:_ The product `minimum_value` or `maximum_value` has changed, orders outside the new range will be rejected |
| 196 | + |
| 197 | +```json Example payload where minimum and maximum values change for an open denomination type {4,14} [expandable] |
| 198 | +{ |
| 199 | + "product_code": "XYZ-US", |
| 200 | + "old_state": { |
| 201 | + "price_multiplier": "1.0", |
| 202 | + "is_orderable": true, |
| 203 | + "denominations": { |
| 204 | + "type": "open", |
| 205 | + "minimum_value": "5", |
| 206 | + "maximum_value": "500" |
| 207 | + } |
| 208 | + }, |
| 209 | + "new_state": { |
| 210 | + "price_multiplier": "1.0", |
| 211 | + "is_orderable": true, |
| 212 | + "denominations": { |
| 213 | + "type": "open", |
| 214 | + "minimum_value": "10", |
| 215 | + "maximum_value": "300" |
| 216 | + } |
| 217 | + }, |
| 218 | + "timestamp": "2024-04-10T17:11:26.601254" |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +<Note> |
| 223 | + When a product becomes non-orderable (`is_orderable: false`), both the |
| 224 | + `denominations` and `price_multiplier` information are preserved and continue |
| 225 | + to show the same values that were available when the product was orderable. |
| 226 | +</Note> |
0 commit comments