@spree/docs 0.1.93 → 0.1.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/api-reference/admin-api/authentication.md +8 -7
  2. package/dist/api-reference/admin-api/endpoints.md +348 -0
  3. package/dist/api-reference/admin-api/errors.md +2 -0
  4. package/dist/api-reference/admin-api/introduction.md +11 -0
  5. package/dist/api-reference/store.yaml +243 -232
  6. package/dist/developer/agentic/overview.md +1 -1
  7. package/dist/developer/cli/admin-api.md +146 -0
  8. package/dist/developer/cli/quickstart.md +40 -5
  9. package/dist/developer/core-concepts/addresses.md +32 -16
  10. package/dist/developer/core-concepts/adjustments.md +11 -5
  11. package/dist/developer/core-concepts/architecture.md +8 -8
  12. package/dist/developer/core-concepts/calculators.md +31 -51
  13. package/dist/developer/core-concepts/channels.md +13 -6
  14. package/dist/developer/core-concepts/customers.md +47 -23
  15. package/dist/developer/core-concepts/events.md +22 -17
  16. package/dist/developer/core-concepts/imports-exports.md +69 -14
  17. package/dist/developer/core-concepts/inventory.md +79 -1
  18. package/dist/developer/core-concepts/markets.md +64 -20
  19. package/dist/developer/core-concepts/media.md +43 -6
  20. package/dist/developer/core-concepts/metafields.md +76 -13
  21. package/dist/developer/core-concepts/orders.md +95 -17
  22. package/dist/developer/core-concepts/payments.md +14 -13
  23. package/dist/developer/core-concepts/pricing.md +95 -9
  24. package/dist/developer/core-concepts/products.md +192 -26
  25. package/dist/developer/core-concepts/promotions.md +61 -4
  26. package/dist/developer/core-concepts/reports.md +4 -2
  27. package/dist/developer/core-concepts/search-filtering.md +82 -32
  28. package/dist/developer/core-concepts/shipments.md +16 -13
  29. package/dist/developer/core-concepts/slugs.md +20 -11
  30. package/dist/developer/core-concepts/staff-roles.md +51 -1
  31. package/dist/developer/core-concepts/store-credits-gift-cards.md +90 -9
  32. package/dist/developer/core-concepts/stores.md +16 -14
  33. package/dist/developer/core-concepts/taxes.md +28 -0
  34. package/dist/developer/core-concepts/translations.md +16 -7
  35. package/dist/developer/core-concepts/users.md +13 -9
  36. package/dist/developer/core-concepts/webhooks.md +95 -64
  37. package/dist/developer/how-to/custom-api-authentication.md +103 -23
  38. package/dist/developer/multi-store/quickstart.md +1 -1
  39. package/dist/developer/sdk/admin/authentication.md +1 -1
  40. package/dist/developer/sdk/admin/resources.md +2 -0
  41. package/dist/developer/upgrades/5.3-to-5.4.md +1 -1
  42. package/dist/developer/upgrades/5.4-to-5.5.md +1 -1
  43. package/dist/integrations/integrations.md +0 -7
  44. package/package.json +1 -1
  45. package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +0 -57
  46. package/dist/integrations/sso-mfa-social-login/storefront.md +0 -56
@@ -83,10 +83,11 @@ Categories help organize store credits by their purpose:
83
83
 
84
84
  | Category | Description |
85
85
  |----------|-------------|
86
- | Default | General purpose store credit |
87
- | Gift Card | Store credit created from gift card redemption |
86
+ | Default | General purpose store credit (also the default reimbursement category) |
87
+ | Expiring | Store credit that may expire |
88
+ | Non-expiring | Store credit that does not expire |
88
89
 
89
- > **INFO:** Categories can be configured as non-expiring. By default, the "Gift Card" category is non-expiring.
90
+ > **INFO:** "Gift Card" is not a seeded category but a special name recognized by the model: any category named "Gift Card" (or one listed in `Spree::Config[:non_expiring_credit_types]`) is treated as non-expiring via `StoreCreditCategory#non_expiring?`.
90
91
 
91
92
  ### Store Credit Types
92
93
 
@@ -120,7 +121,34 @@ Store credits are managed in the Admin Panel:
120
121
  5. Enter the amount, category, and optional memo
121
122
  6. Click **Create**
122
123
 
123
- Store credits can also be created via the Admin API.
124
+ Store credits can also be created via the [Admin API](../../api-reference/admin-api/introduction.md), as a nested resource under the customer:
125
+
126
+
127
+ ```typescript Admin SDK
128
+ import { createAdminClient } from '@spree/admin-sdk'
129
+
130
+ const client = createAdminClient({
131
+ baseUrl: 'https://store.example.com',
132
+ secretKey: 'sk_xxx',
133
+ })
134
+
135
+ const credit = await client.customers.storeCredits.create('cus_xxx', {
136
+ amount: 25.0,
137
+ currency: 'USD',
138
+ category_id: 'sccat_xxx',
139
+ memo: 'Goodwill credit for delayed shipment',
140
+ })
141
+ ```
142
+
143
+ ```bash CLI
144
+ spree api post /customers/cus_xxx/store_credits -d '{
145
+ "amount": 25.0,
146
+ "currency": "USD",
147
+ "category_id": "sccat_xxx",
148
+ "memo": "Goodwill credit"
149
+ }'
150
+ ```
151
+
124
152
 
125
153
  ### Store Credit Events
126
154
 
@@ -151,7 +179,7 @@ erDiagram
151
179
  decimal amount_used
152
180
  decimal amount_authorized
153
181
  string currency
154
- datetime expires_at
182
+ date expires_at
155
183
  datetime redeemed_at
156
184
  }
157
185
 
@@ -160,7 +188,7 @@ erDiagram
160
188
  integer codes_count
161
189
  decimal amount
162
190
  string currency
163
- datetime expires_at
191
+ date expires_at
164
192
  }
165
193
  ```
166
194
 
@@ -218,7 +246,34 @@ flowchart TB
218
246
  4. Click **Create**
219
247
  5. Share the generated code with the recipient
220
248
 
221
- Gift cards can also be created via the Admin API.
249
+ Gift cards can also be created via the [Admin API](../../api-reference/admin-api/introduction.md). The code is generated automatically if you don't supply one:
250
+
251
+
252
+ ```typescript Admin SDK
253
+ import { createAdminClient } from '@spree/admin-sdk'
254
+
255
+ const client = createAdminClient({
256
+ baseUrl: 'https://store.example.com',
257
+ secretKey: 'sk_xxx',
258
+ })
259
+
260
+ const giftCard = await client.giftCards.create({
261
+ amount: 50,
262
+ currency: 'USD',
263
+ expires_at: '2026-12-31',
264
+ })
265
+
266
+ console.log(giftCard.code) // share this with the recipient
267
+ ```
268
+
269
+ ```bash CLI
270
+ spree api post /gift_cards -d '{
271
+ "amount": 50,
272
+ "currency": "USD",
273
+ "expires_at": "2026-12-31"
274
+ }'
275
+ ```
276
+
222
277
 
223
278
  #### Batch Gift Card Generation
224
279
 
@@ -233,6 +288,30 @@ For promotions or bulk distribution, you can create multiple gift cards at once
233
288
  - **Expiration** - Optional expiration date
234
289
  4. Click **Create**
235
290
 
291
+ Batches can also be created via the Admin API:
292
+
293
+
294
+ ```typescript Admin SDK
295
+ const batch = await client.giftCardBatches.create({
296
+ prefix: 'HOLIDAY',
297
+ codes_count: 1000,
298
+ amount: 25,
299
+ currency: 'USD',
300
+ expires_at: '2026-12-31',
301
+ })
302
+ ```
303
+
304
+ ```bash CLI
305
+ spree api post /gift_card_batches -d '{
306
+ "prefix": "HOLIDAY",
307
+ "codes_count": 1000,
308
+ "amount": 25,
309
+ "currency": "USD",
310
+ "expires_at": "2026-12-31"
311
+ }'
312
+ ```
313
+
314
+
236
315
  > **INFO:** Large batches are processed in the background to avoid timeout issues.
237
316
 
238
317
  ### Redeeming Gift Cards
@@ -246,10 +325,10 @@ When a gift card is applied to an order:
246
325
  - The gift card is marked as redeemed (or partially redeemed)
247
326
  - No Store Credit is created for guest checkouts
248
327
 
249
- Gift cards are applied using the same coupon codes endpoint as promotion codes. The endpoint automatically detects whether the code is a promotion coupon or a gift card.
328
+ Gift cards are applied via a dedicated `POST /api/v3/store/carts/:cart_id/gift_cards` endpoint (separate from the `discount_codes` endpoint used for promotion codes); the discount-codes endpoint does not handle gift cards. See the [cart & checkout SDK guide](../sdk/store/cart-checkout.md) for the full cart flow these calls belong to.
250
329
 
251
330
 
252
- ```typescript SDK
331
+ ```typescript Store SDK
253
332
  // Apply gift card code to cart (works for guests and registered customers)
254
333
  // Gift cards use a dedicated endpoint — they reduce amount_due, not total
255
334
  const cart = await client.carts.giftCards.apply('cart_abc123', 'abc1234def', {
@@ -324,3 +403,5 @@ When a registered customer has multiple store credits, they are applied in order
324
403
  - [Orders](orders.md) — Order management
325
404
  - [Customers](customers.md) — Customer management
326
405
  - [Events](events.md) — Event system and subscribers
406
+ - [Admin SDK Quickstart](../sdk/admin/quickstart.md) — Set up and authenticate the Admin SDK
407
+ - [Monetary Amounts](../../api-reference/store-api/monetary-amounts.md) — How the API represents `amount`, `amount_used`, and other money fields
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: Stores
3
- description: The Store is Spree's top-level multi-tenant boundary — learn how products, orders, channels, markets, and stock locations are scoped to a store.
3
+ description: The Store is Spree's top-level multi-tenant boundary — learn how products, orders, channels, and markets are scoped to a store.
4
4
  ---
5
5
 
6
6
  ## Overview
7
7
 
8
- The Store is the top-level tenant in Spree. Every resource — products, orders, channels, markets, taxonomies, stock locations — belongs to exactly one store. A store owns its branding (logo, custom domain, mail-from address), its [channels](channels.md) (online, POS, wholesale, …), its [markets](markets.md) (region/currency/locale), and its catalog.
8
+ The Store is the top-level tenant in Spree. Every resource — products, orders, channels, markets, taxonomies — belongs to exactly one store. A store owns its branding (logo, custom domain, mail-from address), its [channels](channels.md) (online, POS, wholesale, …), its [markets](markets.md) (region/currency/locale), and its catalog.
9
9
 
10
10
  ## Store Attributes
11
11
 
@@ -19,33 +19,34 @@ The Store is the top-level tenant in Spree. Every resource — products, orders,
19
19
  | `seo_title` | Custom SEO title |
20
20
  | `customer_support_email` | Email for customer support inquiries |
21
21
  | `mail_from_address` | Sender address for transactional emails |
22
- | `logo_image_url` | URL to the store's logo |
22
+ | `logo_url` | URL to the store's logo |
23
23
  | `facebook`, `twitter`, `instagram` | Social media links |
24
- | `payment_methods` | Payment methods available in this store |
25
24
 
26
25
  ## Fetching Store Information
27
26
 
28
- Use the store endpoint to get the current store's configurationuseful for rendering logos, SEO metadata, and footer content:
27
+ Store configuration is exposed through the Admin API. Use the store endpoint to read the current store's settingsname, URL, branding, and email addresses:
29
28
 
30
29
 
31
- ```typescript SDK
32
- const store = await client.store.get()
30
+ ```typescript Admin SDK
31
+ const store = await adminClient.store.get()
33
32
  // {
34
33
  // name: "My Store",
35
34
  // url: "https://mystore.com",
36
- // logo_image_url: "https://cdn.mystore.com/logo.png",
35
+ // logo_url: "https://cdn.mystore.com/logo.png",
36
+ // mailer_logo_url: "https://cdn.mystore.com/mailer-logo.png",
37
37
  // customer_support_email: "support@mystore.com",
38
- // payment_methods: [{ id: "pm_xxx", name: "Stripe", kind: "card" }],
39
38
  // ...
40
39
  // }
41
40
  ```
42
41
 
43
42
  ```bash cURL
44
- curl 'https://api.mystore.com/api/v3/store/store' \
45
- -H 'X-Spree-API-Key: pk_xxx'
43
+ curl 'https://api.mystore.com/api/v3/admin/store' \
44
+ -H 'X-Spree-API-Key: sk_xxx'
46
45
  ```
47
46
 
48
47
 
48
+ This is an admin endpoint, so it requires a secret API key (`sk_xxx`) or a Bearer JWT — a publishable key cannot reach it.
49
+
49
50
  ## Channels vs. Markets
50
51
 
51
52
  Two different ways to split a store, often confused:
@@ -65,7 +66,7 @@ Each store owns its own resources. Products, orders, channels, markets, and taxo
65
66
  | [**Markets**](markets.md) | A store has many markets, each defining a geographic region with its own currency and locale |
66
67
  | [**Orders**](orders.md) | An order belongs to one store and one channel |
67
68
  | [**Products**](products.md) | A product belongs to one store. Its visibility across channels is controlled by [publications](channels.md#publishing-products-on-channels). |
68
- | [**Taxonomies**](products.md#taxons-and-taxonomies) | A taxonomy belongs to one store |
69
+ | [**Taxonomies**](products.md#categories) | A taxonomy belongs to one store |
69
70
  | [**Payment Methods**](payments.md) | A payment method belongs to one store |
70
71
  | [**Shipping Methods**](shipments.md) | A shipping method belongs to one store |
71
72
  | [**Promotions**](promotions.md) | A promotion belongs to one store |
@@ -75,9 +76,9 @@ Each store owns its own resources. Products, orders, channels, markets, and taxo
75
76
  If you need one Spree backend to serve **multiple distinct merchant brands** — different domains, different catalogs, different admin teams — there are two patterns:
76
77
 
77
78
  - **Multiple channels under one store** (recommended for most cases) — model each storefront as a Sales Channel. Products are scoped per-channel via publications, orders carry the channel that originated them, and routing/pricing can differ per channel. This is the supported pattern in core Spree 5.5+.
78
- - **Multiple stores in one app** — the legacy multi-store pattern, where each store is fully independent (catalog, admin, branding). In Spree 5.5+ this requires the `spree_multi_store` extension. Core Spree treats each product as belonging to a single store.
79
+ - **Multiple stores in one app** — the legacy multi-store pattern, where each store is fully independent (catalog, admin, branding). In Spree 5.5+ this requires the [spree_multi_store](../multi-store/quickstart.md) extension. Core Spree treats each product as belonging to a single store.
79
80
 
80
- For most multi-brand operations, the channel pattern is simpler and more flexible. Reach for `spree_multi_store` only when stores need genuinely independent catalogs, branding, and admin teams.
81
+ For most multi-brand operations, the channel pattern is simpler and more flexible. Reach for [spree_multi_store](../multi-store/quickstart.md) only when stores need genuinely independent catalogs, branding, and admin teams.
81
82
 
82
83
  ## Related Documentation
83
84
 
@@ -85,3 +86,4 @@ For most multi-brand operations, the channel pattern is simpler and more flexibl
85
86
  - [Markets](markets.md) — Multi-region commerce within a store
86
87
  - [Products](products.md) — Product catalog
87
88
  - [Orders](orders.md) — Order management and checkout
89
+ - [Admin SDK](../sdk/admin/quickstart.md) — TypeScript client for the Admin API used to read and update store configuration
@@ -55,6 +55,33 @@ Each product is assigned a tax category. One category can be set as the default
55
55
  | `is_default` | Whether this is the default category | `true` |
56
56
  | `tax_code` | Code from your tax provider (e.g., Stripe, Avalara) | `1257L` |
57
57
 
58
+ Manage tax categories via the [Admin API](../../api-reference/admin-api/introduction.md), using the [Admin SDK resource clients](../sdk/admin/resources.md):
59
+
60
+
61
+ ```typescript Admin SDK
62
+ import { createAdminClient } from '@spree/admin-sdk'
63
+
64
+ const client = createAdminClient({
65
+ baseUrl: 'https://store.example.com',
66
+ secretKey: 'sk_xxx',
67
+ })
68
+
69
+ const clothing = await client.taxCategories.create({
70
+ name: 'Clothing',
71
+ tax_code: '1257L',
72
+ is_default: true,
73
+ })
74
+
75
+ await client.taxCategories.update('taxcat_xxx', { is_default: false })
76
+ await client.taxCategories.delete('taxcat_xxx')
77
+ ```
78
+
79
+ ```bash CLI
80
+ spree api post /tax_categories -d '{"name": "Clothing", "tax_code": "1257L", "is_default": true}'
81
+ spree api get /tax_categories
82
+ ```
83
+
84
+
58
85
  ## Tax Rates
59
86
 
60
87
  Tax rates define the percentage charged for a specific tax category within a geographic zone.
@@ -133,3 +160,4 @@ Tax categories and rates are managed in the Admin Panel under **Settings → Tax
133
160
  - [Addresses](addresses.md) — Zones and address-based taxation
134
161
  - [Adjustments](adjustments.md) — Tax adjustments
135
162
  - [Orders](orders.md) — How taxes affect order totals
163
+ - [Avalara integration](../../integrations/tax/avalara.md) — Automated tax calculation for complex jurisdictions
@@ -52,23 +52,23 @@ erDiagram
52
52
 
53
53
  | Resource | Translatable Fields |
54
54
  | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
55
- | Product | `name`, `description`, `slug`, `meta_description`, `meta_keywords`, `meta_title` |
55
+ | Product | `name`, `description`, `slug`, `meta_description`, `meta_title` |
56
56
  | Taxon | `name`, `description`, `permalink` |
57
57
  | Taxonomy | `name` |
58
58
  | Option Type | `presentation` |
59
59
  | Option Value | `presentation` |
60
60
  | Property | `presentation` |
61
61
  | Product Property | `value` |
62
- | Store | `name`, `meta_description`, `meta_keywords`, `seo_title`, `facebook`, `twitter`, `instagram`, `customer_support_email`, `description`, `address`, `contact_phone` |
62
+ | Store | `name`, `meta_description`, `meta_keywords`, `seo_title`, `customer_support_email`, `address`, `contact_phone` |
63
63
 
64
64
  ### Store API
65
65
 
66
- To retrieve translated content, pass the locale via the `X-Spree-Locale` header or the SDK `locale` option:
66
+ To retrieve translated content, pass the locale via the [`X-Spree-Locale` header](../../api-reference/store-api/localization.md) or the SDK `locale` option:
67
67
 
68
68
 
69
- ```typescript SDK
69
+ ```typescript Store SDK
70
70
  // Fetch product in French
71
- const product = await client.products.get('spree-tote', {
71
+ const product = await client.products.get('spree-tote', {}, {
72
72
  locale: 'fr',
73
73
  })
74
74
 
@@ -82,6 +82,14 @@ const { data: categories } = await client.categories.list({}, {
82
82
  })
83
83
  ```
84
84
 
85
+ ```typescript Admin SDK
86
+ // Pass the locale as a request option to get translated content.
87
+ // The Admin API resolves by prefixed ID only — slugs are not accepted.
88
+ const product = await adminClient.products.get('prod_86Rf07xd4z', {}, { locale: 'fr' })
89
+
90
+ const { data: categories } = await adminClient.categories.list({}, { locale: 'de' })
91
+ ```
92
+
85
93
  ```bash cURL
86
94
  # Fetch product in French
87
95
  curl 'https://api.mystore.com/api/v3/store/products/spree-tote' \
@@ -96,11 +104,11 @@ Slugs are also localized — a product can have different slugs per locale. See
96
104
 
97
105
  Translations are managed in the Admin Panel. When editing a product, taxon, or other translatable resource, switch the locale selector to enter content in each language.
98
106
 
99
- Translations can also be managed via the Admin API.
107
+ Translations can also be [managed via the Admin API](../sdk/admin/quickstart.md).
100
108
 
101
109
  ## UI Translations
102
110
 
103
- Spree stores UI translation strings in a separate project: [Spree I18n](https://github.com/spree/spree_i18n). This is a community-maintained project with locale files for 43+ languages.
111
+ Spree stores UI translation strings in a separate project: [Spree I18n](https://github.com/spree-contrib/spree_i18n). This is a community-maintained project with locale files for 43+ languages.
104
112
 
105
113
  To install UI translations:
106
114
 
@@ -121,6 +129,7 @@ Once installed, all translation files are available automatically — no need to
121
129
  ## Related Documentation
122
130
 
123
131
  - [Markets](markets.md) — Locale and currency configuration per geographic region
132
+ - [Localization](../../api-reference/store-api/localization.md) — Locale, currency, and country headers in API requests
124
133
  - [Slugs](slugs.md) — Localized slugs and URL identifiers
125
134
  - [Stores](stores.md) — Multi-store setup
126
135
  - [Products](products.md) — Product translations
@@ -57,7 +57,7 @@ erDiagram
57
57
  ### Registration
58
58
 
59
59
 
60
- ```typescript SDK
60
+ ```typescript Store SDK
61
61
  const { token, user } = await client.customers.create({
62
62
  email: 'john@example.com',
63
63
  password: 'password123',
@@ -86,7 +86,7 @@ curl -X POST 'https://api.mystore.com/api/v3/store/customers' \
86
86
  ### Login
87
87
 
88
88
 
89
- ```typescript SDK
89
+ ```typescript Store SDK
90
90
  const { token, user } = await client.auth.login({
91
91
  email: 'john@example.com',
92
92
  password: 'password123',
@@ -105,29 +105,31 @@ curl -X POST 'https://api.mystore.com/api/v3/store/auth/login' \
105
105
  ```
106
106
 
107
107
 
108
- The response includes a JWT `token` and a `user` object. Pass the token in subsequent requests via the `Authorization: Bearer <token>` header.
108
+ The response includes a [JWT token](../../api-reference/store-api/authentication.md) and a `user` object. Pass the token in subsequent requests via the `Authorization: Bearer <token>` header. See the [account SDK guide](../sdk/store/account.md) for the full `auth.login`, refresh, and profile flows.
109
109
 
110
110
  ### Token Refresh
111
111
 
112
112
  Refresh an expiring token to keep the session alive:
113
113
 
114
114
 
115
- ```typescript SDK
115
+ ```typescript Store SDK
116
116
  const { token } = await client.auth.refresh({
117
- token: existingToken,
117
+ refresh_token: existingRefreshToken,
118
118
  })
119
119
  ```
120
120
 
121
121
  ```bash cURL
122
122
  curl -X POST 'https://api.mystore.com/api/v3/store/auth/refresh' \
123
- -H 'Authorization: Bearer <jwt_token>'
123
+ -H 'X-Spree-API-Key: pk_xxx' \
124
+ -H 'Content-Type: application/json' \
125
+ -d '{ "refresh_token": "rt_xxx" }'
124
126
  ```
125
127
 
126
128
 
127
129
  ### Customer Profile
128
130
 
129
131
 
130
- ```typescript SDK
132
+ ```typescript Store SDK
131
133
  // Get current customer
132
134
  const customer = await client.customer.get()
133
135
  // {
@@ -149,11 +151,11 @@ const updated = await client.customer.update({
149
151
 
150
152
  ```bash cURL
151
153
  # Get current customer
152
- curl 'https://api.mystore.com/api/v3/store/customer' \
154
+ curl 'https://api.mystore.com/api/v3/store/customers/me' \
153
155
  -H 'Authorization: Bearer <jwt_token>'
154
156
 
155
157
  # Update profile
156
- curl -X PATCH 'https://api.mystore.com/api/v3/store/customer' \
158
+ curl -X PATCH 'https://api.mystore.com/api/v3/store/customers/me' \
157
159
  -H 'Authorization: Bearer <jwt_token>' \
158
160
  -H 'Content-Type: application/json' \
159
161
  -d '{ "first_name": "Jonathan", "accepts_email_marketing": true }'
@@ -297,3 +299,5 @@ See the [Customize Permissions guide](../customization/permissions.md) for detai
297
299
  - [Authentication](../customization/authentication.md) — Custom authentication setup
298
300
  - [Permissions](../customization/permissions.md) — Roles and authorization
299
301
  - [Events](events.md) — Subscribe to user and invitation events
302
+ - [Store API Authentication](../../api-reference/store-api/authentication.md) — Customer JWT auth flow
303
+ - [Admin API Authentication](../../api-reference/admin-api/authentication.md) — Staff JWT + scopes auth flow
@@ -56,20 +56,39 @@ flowchart LR
56
56
 
57
57
  Navigate to **Settings → Developers → Webhooks** in the admin panel to create and manage webhook endpoints.
58
58
 
59
- ### Via Code
59
+ ### Via the Admin API
60
60
 
61
- ```ruby
62
- endpoint = Spree::WebhookEndpoint.create!(
63
- store: Spree::Store.default,
61
+
62
+ ```typescript Admin SDK
63
+ import { createAdminClient } from '@spree/admin-sdk'
64
+
65
+ const client = createAdminClient({
66
+ baseUrl: 'https://store.example.com',
67
+ secretKey: 'sk_xxx',
68
+ })
69
+
70
+ const endpoint = await client.webhookEndpoints.create({
64
71
  url: 'https://example.com/webhooks/spree',
65
72
  subscriptions: ['order.*', 'product.created'],
66
- active: true
67
- )
73
+ active: true,
74
+ })
75
+
76
+ // secret_key is auto-generated and returned ONLY on create —
77
+ // store it now for HMAC signature verification.
78
+ endpoint.secret_key // => "a1b2c3d4e5f6..." (64-character hex string)
79
+ ```
68
80
 
69
- # The secret_key is auto-generated
70
- endpoint.secret_key # => "a1b2c3d4e5f6..." (64-character hex string)
81
+ ```bash CLI
82
+ spree api post /webhook_endpoints -d '{
83
+ "url": "https://example.com/webhooks/spree",
84
+ "subscriptions": ["order.*", "product.created"],
85
+ "active": true
86
+ }'
71
87
  ```
72
88
 
89
+
90
+ > **WARNING:** The `secret_key` is returned **only once**, in the create response. Save it immediately — it can't be retrieved later and is required to verify webhook signatures.
91
+
73
92
  ### Endpoint Attributes
74
93
 
75
94
  | Attribute | Type | Description |
@@ -78,41 +97,35 @@ endpoint.secret_key # => "a1b2c3d4e5f6..." (64-character hex string)
78
97
  | `active` | Boolean | Enable/disable delivery to this endpoint |
79
98
  | `subscriptions` | Array | Event patterns to subscribe to |
80
99
  | `secret_key` | String | Auto-generated key for HMAC signature verification |
81
- | `store_id` | Integer | The store this endpoint belongs to |
100
+
101
+ Endpoints always belong to the current store — the association is set automatically from the request scope, so you never pass it when creating or updating an endpoint.
82
102
 
83
103
  ## Event Subscriptions
84
104
 
85
- The `subscriptions` attribute controls which events trigger webhooks to this endpoint.
105
+ The `subscriptions` attribute controls which events trigger webhooks to this endpoint. Set it when creating the endpoint, or change it later:
86
106
 
87
- ### Subscribe to Specific Events
88
107
 
89
- ```ruby
90
- endpoint.subscriptions = ['order.completed', 'order.canceled']
108
+ ```typescript Admin SDK
109
+ await client.webhookEndpoints.update('whe_xxx', {
110
+ subscriptions: ['order.completed', 'order.canceled'],
111
+ })
91
112
  ```
92
113
 
93
- ### Subscribe to Event Patterns
94
-
95
- Use wildcards to subscribe to multiple related events:
96
-
97
- ```ruby
98
- # All order events
99
- endpoint.subscriptions = ['order.*']
100
-
101
- # All creation events
102
- endpoint.subscriptions = ['*.created']
103
-
104
- # Multiple patterns
105
- endpoint.subscriptions = ['order.*', 'payment.*', 'shipment.shipped']
114
+ ```bash CLI
115
+ spree api patch /webhook_endpoints/whe_xxx \
116
+ -d '{"subscriptions": ["order.completed", "order.canceled"]}'
106
117
  ```
107
118
 
108
- ### Subscribe to All Events
109
119
 
110
- Leave subscriptions empty or use `*` to receive all events:
120
+ The `subscriptions` array accepts exact event names and wildcard patterns:
111
121
 
112
- ```ruby
113
- endpoint.subscriptions = [] # All events
114
- endpoint.subscriptions = ['*'] # All events (explicit)
115
- ```
122
+ | `subscriptions` value | Receives |
123
+ |---|---|
124
+ | `['order.completed', 'order.canceled']` | Only those two events |
125
+ | `['order.*']` | All order events |
126
+ | `['*.created']` | All creation events |
127
+ | `['order.*', 'payment.*', 'shipment.shipped']` | Multiple patterns |
128
+ | `[]` or `['*']` | All events |
116
129
 
117
130
  ## Webhook Payload
118
131
 
@@ -126,10 +139,11 @@ Each webhook delivery sends a JSON payload with the following structure. The `da
126
139
  "data": {
127
140
  "id": "or_m3Rp9wXz",
128
141
  "number": "R123456789",
129
- "state": "complete",
142
+ "fulfillment_status": "shipped",
143
+ "payment_status": "paid",
130
144
  "total": "99.99",
131
145
  "display_total": "$99.99",
132
- "item_count": 3,
146
+ "total_quantity": 3,
133
147
  "currency": "USD",
134
148
  "items": [ ... ],
135
149
  "fulfillments": [ ... ],
@@ -163,6 +177,7 @@ Each webhook request includes these headers:
163
177
  | `User-Agent` | `Spree-Webhooks/1.0` |
164
178
  | `X-Spree-Webhook-Event` | Event name (e.g., `order.completed`) |
165
179
  | `X-Spree-Webhook-Signature` | HMAC-SHA256 signature for verification |
180
+ | `X-Spree-Webhook-Timestamp` | Unix timestamp (seconds) generated at send time; included in the HMAC-SHA256 signed string as `{timestamp}.{payload}` and required to verify `X-Spree-Webhook-Signature` |
166
181
 
167
182
  ### Verifying Webhook Signatures
168
183
 
@@ -244,21 +259,23 @@ Failed webhook deliveries automatically retry up to 5 times with exponential bac
244
259
 
245
260
  ### Checking Delivery Status
246
261
 
247
- ```ruby
248
- endpoint = Spree::WebhookEndpoint.find(id)
262
+ Inspect an endpoint's delivery log, and re-send a failed delivery, via the Admin API:
249
263
 
250
- # Recent deliveries
251
- endpoint.webhook_deliveries.recent
252
264
 
253
- # Filter by status
254
- endpoint.webhook_deliveries.successful
255
- endpoint.webhook_deliveries.failed
256
- endpoint.webhook_deliveries.pending
265
+ ```typescript Admin SDK
266
+ // Recent deliveries for an endpoint
267
+ const { data: deliveries } = await client.webhookEndpoints.deliveries.list('whe_xxx')
268
+
269
+ // Re-send a specific delivery
270
+ await client.webhookEndpoints.deliveries.redeliver('whe_xxx', 'whd_xxx')
271
+ ```
257
272
 
258
- # Filter by event
259
- endpoint.webhook_deliveries.for_event('order.completed')
273
+ ```bash CLI
274
+ spree api get /webhook_endpoints/whe_xxx/deliveries -q event_name_eq=order.completed
275
+ spree api post /webhook_endpoints/whe_xxx/deliveries/whd_xxx/redeliver
260
276
  ```
261
277
 
278
+
262
279
  ### Delivery Attributes
263
280
 
264
281
  | Attribute | Description |
@@ -309,31 +326,38 @@ Common webhook events include:
309
326
  | `payment.paid` | Payment was completed |
310
327
  | `product.created` | New product created |
311
328
  | `product.updated` | Product was modified |
312
- | `customer.created` | New customer registered |
329
+ | `user.created` | New customer/user registered (the admin user class emits `admin.created`) |
313
330
 
314
331
  ## Testing Webhooks
315
332
 
316
333
  ### In Development
317
334
 
318
- Use tools like [ngrok](https://ngrok.com) or [webhook.site](https://webhook.site) to test webhooks locally:
335
+ Use tools like [ngrok](https://ngrok.com) or [webhook.site](https://webhook.site) to test webhooks locally. Create a test endpoint pointed at the tunnel:
319
336
 
320
- ```ruby
321
- # Create a test endpoint
322
- endpoint = Spree::WebhookEndpoint.create!(
323
- store: Spree::Store.default,
337
+
338
+ ```typescript Admin SDK
339
+ const endpoint = await client.webhookEndpoints.create({
324
340
  url: 'https://your-ngrok-url.ngrok.io/webhooks',
325
341
  subscriptions: ['order.*'],
326
- active: true
327
- )
342
+ active: true,
343
+ })
328
344
 
329
- # Trigger an event
330
- order = Spree::Order.complete.last
331
- order.publish_event('order.completed')
345
+ // Fire a synthetic delivery to confirm it's reachable
346
+ await client.webhookEndpoints.sendTest(endpoint.id)
347
+ ```
332
348
 
333
- # Check delivery
334
- endpoint.webhook_deliveries.last.success?
349
+ ```bash CLI
350
+ spree api post /webhook_endpoints -d '{
351
+ "url": "https://your-ngrok-url.ngrok.io/webhooks",
352
+ "subscriptions": ["order.*"],
353
+ "active": true
354
+ }'
355
+ spree api post /webhook_endpoints/whe_xxx/send_test
335
356
  ```
336
357
 
358
+
359
+ `send_test` delivers a synthetic `webhook.test` event so you can verify the endpoint is reachable and your signature-verification code works, without having to trigger a real order.
360
+
337
361
  ### In Tests
338
362
 
339
363
  ```ruby
@@ -381,18 +405,25 @@ end
381
405
 
382
406
  ### Deliveries Failing
383
407
 
384
- Check the delivery record for details:
408
+ Check the delivery records for details — each carries `error_type`, `request_errors`, `response_code`, and `response_body`. Filter the log with [Ransack predicates](../../api-reference/admin-api/querying.md) such as `success_eq=false` or `event_name_eq`:
385
409
 
386
- ```ruby
387
- delivery = endpoint.webhook_deliveries.failed.last
388
- delivery.error_type # => 'timeout' or 'connection_error'
389
- delivery.request_errors # => Error message
390
- delivery.response_code # => HTTP status code
391
- delivery.response_body # => Response from your endpoint
410
+
411
+ ```typescript Admin SDK
412
+ const { data: deliveries } = await client.webhookEndpoints.deliveries.list('whe_xxx', {
413
+ success_eq: false,
414
+ })
415
+ const failed = deliveries[0]
416
+ // failed.error_type, failed.request_errors, failed.response_code, failed.response_body
417
+ ```
418
+
419
+ ```bash CLI
420
+ spree api get /webhook_endpoints/whe_xxx/deliveries -q success_eq=false
392
421
  ```
393
422
 
423
+
394
424
  ## Related Documentation
395
425
 
396
426
  - [Events](events.md) - Understanding Spree's event system
427
+ - [Admin SDK](../sdk/admin/quickstart.md) - Setting up the `@spree/admin-sdk` client used in the management examples above
397
428
  - [Customization Quickstart](../customization/quickstart.md) - Overview of all customization options
398
429
  - [Dependencies](../customization/dependencies.md) - Customizing Spree services