Skip to content

Commit e367ea6

Browse files
Merge pull request #12 from wegift/product.update-webhook
Extract a feature guide for product.update webhook and update the OpenAPI spec
2 parents e6e90d6 + 266ab47 commit e367ea6

File tree

5 files changed

+252
-230
lines changed

5 files changed

+252
-230
lines changed

docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"group": "Features",
5656
"pages": [
5757
"features/product-catalog",
58+
"features/realtime-product-updates",
5859
"features/estimate",
5960
"features/balances",
6061
"features/limiting-available-products",

features/product-catalog.mdx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,18 @@ The response will include a `pagination` object with the following fields. You c
220220
>
221221
More details on how pagination works in the Runa API.
222222
</Card>
223+
224+
## Keeping your catalog in sync
225+
226+
If you maintain a copy of the product catalog in your system you can use the [product update webhook](/reference/2024-02-05/webhook/product.update) to keep it synchronized and avoid order failures.
227+
228+
<Card
229+
title="How to implement real-time product updates"
230+
href="/features/realtime-product-updates"
231+
icon="webhook"
232+
horizontal
233+
arrow
234+
>
235+
Next take a read of our guide to implement real-time product updates in your
236+
system.
237+
</Card>
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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>

reference/2024-02-05/openapi.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,10 +1587,12 @@
15871587
"required": ["price_multiplier", "is_orderable"],
15881588
"properties": {
15891589
"price_multiplier": {
1590-
"type": "string",
1591-
"example": "0.8"
1590+
"$ref": "#/components/schemas/ProductDiscountMultiplier"
15921591
},
1593-
"is_orderable": { "$ref": "#/components/schemas/ProductIsOrderable" }
1592+
"is_orderable": { "$ref": "#/components/schemas/ProductIsOrderable" },
1593+
"denominations": {
1594+
"$ref": "#/components/schemas/ProductDenominations"
1595+
}
15941596
}
15951597
},
15961598
"PayoutStatus": {
@@ -1955,7 +1957,8 @@
19551957
"type": {
19561958
"type": "string",
19571959
"enum": ["open", "fixed"],
1958-
"description": "The type of denomination. Can be `open` or `fixed`. `open` is any amount between minimum and maximum value. Fixed is limited to those returned in the `available_list`."
1960+
"description": "The type of denomination. Can be `open` or `fixed`. `open` is any amount between minimum and maximum value. Fixed is limited to those returned in the `available_list`.",
1961+
"example": "fixed"
19591962
},
19601963
"minimum_value": {
19611964
"$ref": "#/components/schemas/ProductMinimumValue"

0 commit comments

Comments
 (0)