@spree/docs 0.1.0

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 (183) hide show
  1. package/README.md +54 -0
  2. package/dist/api-reference/platform/authentication.md +38 -0
  3. package/dist/api-reference/store-api/authentication.md +188 -0
  4. package/dist/api-reference/store-api/errors.md +277 -0
  5. package/dist/api-reference/store-api/idempotency.md +129 -0
  6. package/dist/api-reference/store-api/introduction.md +34 -0
  7. package/dist/api-reference/store-api/localization.md +279 -0
  8. package/dist/api-reference/store-api/metadata.md +160 -0
  9. package/dist/api-reference/store-api/monetary-amounts.md +65 -0
  10. package/dist/api-reference/store-api/querying.md +399 -0
  11. package/dist/api-reference/store-api/rate-limitting.md +103 -0
  12. package/dist/api-reference/store-api/relations.md +185 -0
  13. package/dist/api-reference/storefront/authentication.md +88 -0
  14. package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
  15. package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
  16. package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
  17. package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
  18. package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
  19. package/dist/api-reference/v2/introduction.md +22 -0
  20. package/dist/api-reference/v2/pagination.md +37 -0
  21. package/dist/api-reference/webhooks-events.md +883 -0
  22. package/dist/developer/admin/admin.md +205 -0
  23. package/dist/developer/admin/authentication.md +59 -0
  24. package/dist/developer/admin/components.md +711 -0
  25. package/dist/developer/admin/custom-css.md +243 -0
  26. package/dist/developer/admin/custom-javascript.md +116 -0
  27. package/dist/developer/admin/extending-ui.md +1964 -0
  28. package/dist/developer/admin/form-builder.md +444 -0
  29. package/dist/developer/admin/helper-methods.md +531 -0
  30. package/dist/developer/admin/navigation.md +805 -0
  31. package/dist/developer/admin/tables.md +491 -0
  32. package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
  33. package/dist/developer/cli/quickstart.md +137 -0
  34. package/dist/developer/contributing/creating-an-extension.md +258 -0
  35. package/dist/developer/contributing/developing-spree.md +339 -0
  36. package/dist/developer/contributing/quickstart.md +32 -0
  37. package/dist/developer/contributing/updating-extensions.md +67 -0
  38. package/dist/developer/core-concepts/addresses.md +265 -0
  39. package/dist/developer/core-concepts/adjustments.md +107 -0
  40. package/dist/developer/core-concepts/architecture.md +177 -0
  41. package/dist/developer/core-concepts/calculators.md +323 -0
  42. package/dist/developer/core-concepts/customers.md +230 -0
  43. package/dist/developer/core-concepts/events.md +624 -0
  44. package/dist/developer/core-concepts/imports-exports.md +698 -0
  45. package/dist/developer/core-concepts/inventory.md +191 -0
  46. package/dist/developer/core-concepts/markets.md +250 -0
  47. package/dist/developer/core-concepts/media.md +167 -0
  48. package/dist/developer/core-concepts/metafields.md +187 -0
  49. package/dist/developer/core-concepts/orders.md +328 -0
  50. package/dist/developer/core-concepts/payments.md +710 -0
  51. package/dist/developer/core-concepts/pricing.md +163 -0
  52. package/dist/developer/core-concepts/products.md +360 -0
  53. package/dist/developer/core-concepts/promotions.md +322 -0
  54. package/dist/developer/core-concepts/reports.md +206 -0
  55. package/dist/developer/core-concepts/search-filtering.md +237 -0
  56. package/dist/developer/core-concepts/shipments.md +212 -0
  57. package/dist/developer/core-concepts/slugs.md +111 -0
  58. package/dist/developer/core-concepts/staff-roles.md +123 -0
  59. package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
  60. package/dist/developer/core-concepts/stores.md +117 -0
  61. package/dist/developer/core-concepts/taxes.md +135 -0
  62. package/dist/developer/core-concepts/translations.md +120 -0
  63. package/dist/developer/core-concepts/users.md +299 -0
  64. package/dist/developer/core-concepts/webhooks.md +378 -0
  65. package/dist/developer/create-spree-app/quickstart.md +158 -0
  66. package/dist/developer/customization/api.md +93 -0
  67. package/dist/developer/customization/authentication.md +88 -0
  68. package/dist/developer/customization/checkout.md +204 -0
  69. package/dist/developer/customization/configuration.md +55 -0
  70. package/dist/developer/customization/decorators.md +523 -0
  71. package/dist/developer/customization/dependencies.md +232 -0
  72. package/dist/developer/customization/emails.md +21 -0
  73. package/dist/developer/customization/extensions.md +92 -0
  74. package/dist/developer/customization/metadata.md +236 -0
  75. package/dist/developer/customization/model-preferences.md +130 -0
  76. package/dist/developer/customization/permissions.md +265 -0
  77. package/dist/developer/customization/quickstart.md +229 -0
  78. package/dist/developer/customization/routes.md +24 -0
  79. package/dist/developer/customization/v4/admin-panel.md +78 -0
  80. package/dist/developer/customization/v4/authentication.md +210 -0
  81. package/dist/developer/customization/v4/checkout.md +212 -0
  82. package/dist/developer/customization/v4/deface.md +251 -0
  83. package/dist/developer/customization/v4/images.md +86 -0
  84. package/dist/developer/customization/v4/storefront.md +450 -0
  85. package/dist/developer/deployment/assets.md +87 -0
  86. package/dist/developer/deployment/aws.md +335 -0
  87. package/dist/developer/deployment/caching.md +27 -0
  88. package/dist/developer/deployment/cdn.md +39 -0
  89. package/dist/developer/deployment/database.md +155 -0
  90. package/dist/developer/deployment/docker.md +128 -0
  91. package/dist/developer/deployment/emails.md +77 -0
  92. package/dist/developer/deployment/environment_variables.md +111 -0
  93. package/dist/developer/deployment/heroku.md +51 -0
  94. package/dist/developer/deployment/render.md +95 -0
  95. package/dist/developer/getting-started/quickstart.md +82 -0
  96. package/dist/developer/how-to/custom-payment-method.md +374 -0
  97. package/dist/developer/how-to/custom-promotion.md +373 -0
  98. package/dist/developer/how-to/custom-report.md +387 -0
  99. package/dist/developer/how-to/custom-search-provider.md +230 -0
  100. package/dist/developer/multi-store/quickstart.md +71 -0
  101. package/dist/developer/multi-store/setup.md +38 -0
  102. package/dist/developer/multi-tenant/configuration.md +41 -0
  103. package/dist/developer/multi-tenant/core-concepts.md +75 -0
  104. package/dist/developer/multi-tenant/installation.md +96 -0
  105. package/dist/developer/multi-tenant/quickstart.md +20 -0
  106. package/dist/developer/multi-vendor/installation.md +45 -0
  107. package/dist/developer/multi-vendor/quickstart.md +17 -0
  108. package/dist/developer/sdk/admin/quickstart.md +22 -0
  109. package/dist/developer/sdk/authentication.md +89 -0
  110. package/dist/developer/sdk/configuration.md +225 -0
  111. package/dist/developer/sdk/quickstart.md +82 -0
  112. package/dist/developer/sdk/store/account.md +67 -0
  113. package/dist/developer/sdk/store/cart-checkout.md +140 -0
  114. package/dist/developer/sdk/store/markets.md +151 -0
  115. package/dist/developer/sdk/store/payments.md +96 -0
  116. package/dist/developer/sdk/store/products.md +149 -0
  117. package/dist/developer/sdk/store/wishlists.md +52 -0
  118. package/dist/developer/security/pci_compliance.md +15 -0
  119. package/dist/developer/security/security_policy.md +68 -0
  120. package/dist/developer/storefront/blocks.md +285 -0
  121. package/dist/developer/storefront/custom-css.md +260 -0
  122. package/dist/developer/storefront/custom-javascript.md +166 -0
  123. package/dist/developer/storefront/helper-methods.md +1288 -0
  124. package/dist/developer/storefront/links.md +298 -0
  125. package/dist/developer/storefront/nextjs/architecture.md +150 -0
  126. package/dist/developer/storefront/nextjs/customization.md +141 -0
  127. package/dist/developer/storefront/nextjs/deployment.md +180 -0
  128. package/dist/developer/storefront/nextjs/quickstart.md +92 -0
  129. package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
  130. package/dist/developer/storefront/pages.md +163 -0
  131. package/dist/developer/storefront/sections.md +569 -0
  132. package/dist/developer/storefront/storefront.md +56 -0
  133. package/dist/developer/storefront/themes.md +161 -0
  134. package/dist/developer/tutorial/admin.md +134 -0
  135. package/dist/developer/tutorial/extending-models.md +380 -0
  136. package/dist/developer/tutorial/file-uploads.md +121 -0
  137. package/dist/developer/tutorial/introduction.md +33 -0
  138. package/dist/developer/tutorial/model.md +41 -0
  139. package/dist/developer/tutorial/page-builder.md +487 -0
  140. package/dist/developer/tutorial/rich-text.md +73 -0
  141. package/dist/developer/tutorial/seo.md +332 -0
  142. package/dist/developer/tutorial/storefront.md +352 -0
  143. package/dist/developer/tutorial/testing.md +558 -0
  144. package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
  145. package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
  146. package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
  147. package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
  148. package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
  149. package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
  150. package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
  151. package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
  152. package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
  153. package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
  154. package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
  155. package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
  156. package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
  157. package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
  158. package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
  159. package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
  160. package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
  161. package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
  162. package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
  163. package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
  164. package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
  165. package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
  166. package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
  167. package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
  168. package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
  169. package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
  170. package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
  171. package/dist/developer/upgrades/quickstart.md +36 -0
  172. package/dist/integrations/analytics/google-analytics.md +64 -0
  173. package/dist/integrations/analytics/google-tag-manager.md +78 -0
  174. package/dist/integrations/integrations.md +39 -0
  175. package/dist/integrations/marketing/klaviyo.md +99 -0
  176. package/dist/integrations/payments/adyen.md +90 -0
  177. package/dist/integrations/payments/paypal.md +41 -0
  178. package/dist/integrations/payments/razorpay.md +45 -0
  179. package/dist/integrations/payments/stripe.md +109 -0
  180. package/dist/integrations/search/meilisearch.md +236 -0
  181. package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
  182. package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
  183. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @spree/docs
2
+
3
+ Spree Commerce developer documentation packaged for local access by AI agents and development tools.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @spree/docs
9
+ # or
10
+ pnpm add @spree/docs
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Documentation files are plain Markdown, accessible at:
16
+
17
+ ```
18
+ node_modules/@spree/docs/dist/
19
+ ├── developer/
20
+ │ ├── core-concepts/ # Products, orders, payments, inventory, etc.
21
+ │ ├── customization/ # Decorators, extensions, configuration
22
+ │ ├── admin/ # Admin panel customization
23
+ │ ├── storefront/ # Storefront building guides
24
+ │ ├── sdk/ # TypeScript SDK documentation
25
+ │ ├── deployment/ # Deployment guides
26
+ │ └── tutorial/ # Step-by-step tutorials
27
+ ├── api-reference/
28
+ │ └── store-api/ # Store API reference
29
+ └── integrations/ # Third-party integration guides
30
+ ```
31
+
32
+ ### For AI Agents
33
+
34
+ Point your `CLAUDE.md` or agent configuration to the docs:
35
+
36
+ ```markdown
37
+ ## Spree Documentation
38
+ Full developer docs: `node_modules/@spree/docs/dist/`
39
+ ```
40
+
41
+ ### Programmatic Access (Node.js)
42
+
43
+ ```javascript
44
+ import { readFileSync } from 'fs'
45
+ import { createRequire } from 'module'
46
+
47
+ const require = createRequire(import.meta.url)
48
+ const docsPath = require.resolve('@spree/docs/dist/developer/core-concepts/products.md')
49
+ const content = readFileSync(docsPath, 'utf-8')
50
+ ```
51
+
52
+ ## License
53
+
54
+ CC-BY-4.0 — same as the Spree Commerce documentation.
@@ -0,0 +1,38 @@
1
+ Platform API is meant to be used by external applications to perform operations within Spree. Because of that, it uses a different authentication schema than the Storefront API.
2
+
3
+ ### Creating an application inside Spree
4
+
5
+ To generate a valid token for the Platform API, you'll first need to create an application inside Spree.
6
+
7
+ 1. Go to Spree's admin panel and open `Apps` -> `oAuth Applications` in the menu
8
+
9
+ <img src="/images/Platform API 1.png"/>
10
+
11
+ 2. Click on `New oAuth Application`
12
+
13
+ <img src="/images/Platform API 2.png"/>
14
+
15
+ 3. Give your application a name and click `Create`
16
+
17
+ <img src="/images/Platform API 3.png"/>
18
+
19
+ 4. Save your Client ID and Secret - you'll later use it to generate a OAuth token to access the platform API
20
+
21
+ <img src="/images/Platform API 4.png"/>
22
+
23
+ ### Generating OAuth token
24
+
25
+ Once the application is configured inside Spree, you can use its credentials to generate an OAuth token that will give you access to the APIs.
26
+
27
+ To obtain the token, send the following `POST` request to `/spree_oauth/token`
28
+
29
+ ```json
30
+ {
31
+ "grant_type": "client_credentials",
32
+ "client_id": "xxx",
33
+ "client_secret": "xxx",
34
+ "scope": "admin"
35
+ }
36
+ ```
37
+
38
+ In the response, you'll receive a token to pass in `Authorization: Bearer {token}` header when making requests to the Platform API.
@@ -0,0 +1,188 @@
1
+ ---
2
+ title: "Authentication"
3
+ sidebarTitle: "Authentication"
4
+ description: "How to authenticate requests to the Store API"
5
+ ---
6
+
7
+ The Store API uses three authentication mechanisms depending on the use case: **API keys** for all requests, **JWT tokens** for authenticated customers, and **order tokens** for guest checkout.
8
+
9
+ ## API Key (Required)
10
+
11
+ Every request to the Store API requires a **publishable API key**. This key identifies your storefront and is safe to use in client-side code.
12
+
13
+ Pass the key via the `X-Spree-Api-Key` header:
14
+
15
+
16
+ ```typescript SDK
17
+ import { createClient } from '@spree/sdk'
18
+
19
+ const client = createClient({
20
+ baseUrl: 'http://localhost:3000',
21
+ publishableKey: 'spree_pk_xxx',
22
+ })
23
+
24
+ // The SDK automatically sends the publishable key with every request
25
+ const products = await client.products.list()
26
+ ```
27
+
28
+ ```bash cURL
29
+ curl -X GET 'http://localhost:3000/api/v3/store/products' \
30
+ -H 'X-Spree-Api-Key: spree_pk_xxx'
31
+ ```
32
+
33
+
34
+ Publishable API keys are prefixed with `spree_pk_`. You can create them in the Spree Admin under **Settings > API Keys** or via the Spree CLI:
35
+
36
+ ```bash
37
+ spree api-key create # Create a new API key
38
+ spree api-key list # List existing API keys
39
+ ```
40
+
41
+ > **WARNING:** If you omit the API key, the API returns a `401 Unauthorized` error:
42
+ >
43
+ > ```json
44
+ {
45
+ "error": {
46
+ "code": "invalid_token",
47
+ "message": "Valid API key required"
48
+ }
49
+ }
50
+ ```
51
+
52
+ ## JWT Token (Authenticated Customer)
53
+
54
+ For actions that require a logged-in customer (viewing orders, managing addresses, saved payment methods), use a **JWT bearer token** in addition to the API key.
55
+
56
+ ### Login
57
+
58
+
59
+ ```typescript SDK
60
+ // Login to get a JWT token
61
+ const { token, user } = await client.auth.login({
62
+ email: 'customer@example.com',
63
+ password: 'password123',
64
+ })
65
+
66
+ // Use the token for authenticated requests
67
+ const orders = await client.customer.orders.list({}, { token })
68
+ ```
69
+
70
+ ```bash cURL
71
+ # Login
72
+ curl -X POST 'http://localhost:3000/api/v3/store/auth/login' \
73
+ -H 'X-Spree-Api-Key: spree_pk_xxx' \
74
+ -H 'Content-Type: application/json' \
75
+ -d '{"email": "customer@example.com", "password": "password123"}'
76
+
77
+ # Use the returned token for authenticated requests
78
+ curl -X GET 'http://localhost:3000/api/v3/store/orders' \
79
+ -H 'X-Spree-Api-Key: spree_pk_xxx' \
80
+ -H 'Authorization: Bearer <jwt_token>'
81
+ ```
82
+
83
+
84
+ ### Register
85
+
86
+
87
+ ```typescript SDK
88
+ const { token, user } = await client.customers.create({
89
+ email: 'new@example.com',
90
+ password: 'password123',
91
+ password_confirmation: 'password123',
92
+ first_name: 'John',
93
+ last_name: 'Doe',
94
+ })
95
+ ```
96
+
97
+ ```bash cURL
98
+ curl -X POST 'http://localhost:3000/api/v3/store/customers' \
99
+ -H 'X-Spree-Api-Key: spree_pk_xxx' \
100
+ -H 'Content-Type: application/json' \
101
+ -d '{
102
+ "email": "new@example.com",
103
+ "password": "password123",
104
+ "password_confirmation": "password123",
105
+ "first_name": "John",
106
+ "last_name": "Doe"
107
+ }'
108
+ ```
109
+
110
+
111
+ ### Token Refresh
112
+
113
+ JWT tokens expire after 1 hour by default. Use the refresh endpoint to get a new token:
114
+
115
+
116
+ ```typescript SDK
117
+ const { token } = await client.auth.refresh({ token: currentToken })
118
+ ```
119
+
120
+ ```bash cURL
121
+ curl -X POST 'http://localhost:3000/api/v3/store/auth/refresh' \
122
+ -H 'X-Spree-Api-Key: spree_pk_xxx' \
123
+ -H 'Authorization: Bearer <current_jwt_token>'
124
+ ```
125
+
126
+
127
+ ## Order Token (Guest Checkout)
128
+
129
+ For guest checkout flows, use the **order token** returned when creating a cart. This allows unauthenticated users to manage their cart and complete checkout.
130
+
131
+ Pass the token via the `x-spree-token` header:
132
+
133
+
134
+ ```typescript SDK
135
+ // Create a cart (guest)
136
+ const cart = await client.carts.create()
137
+
138
+ // Use spreeToken for all guest cart operations
139
+ const options = { spreeToken: cart.token }
140
+
141
+ // Add items
142
+ await client.carts.items.create(cart.id, {
143
+ variant_id: 'variant_abc123',
144
+ quantity: 1,
145
+ }, options)
146
+ ```
147
+
148
+ ```bash cURL
149
+ # Create a cart
150
+ curl -X POST 'http://localhost:3000/api/v3/store/carts' \
151
+ -H 'X-Spree-Api-Key: spree_pk_xxx'
152
+
153
+ # Use the returned token for cart operations
154
+ curl -X POST 'http://localhost:3000/api/v3/store/carts/cart_xxx/items' \
155
+ -H 'X-Spree-Api-Key: spree_pk_xxx' \
156
+ -H 'X-Spree-Token: <order_token>' \
157
+ -H 'Content-Type: application/json' \
158
+ -d '{"variant_id": "variant_abc123", "quantity": 1}'
159
+ ```
160
+
161
+
162
+ ### Associating a Guest Cart
163
+
164
+ After a guest user logs in, you can associate their guest cart with their account:
165
+
166
+
167
+ ```typescript SDK
168
+ await client.carts.associate(cart.id, {
169
+ token: jwtToken,
170
+ spreeToken: cart.token,
171
+ })
172
+ ```
173
+
174
+ ```bash cURL
175
+ curl -X PATCH 'http://localhost:3000/api/v3/store/carts/cart_xxx/associate' \
176
+ -H 'X-Spree-Api-Key: spree_pk_xxx' \
177
+ -H 'Authorization: Bearer <jwt_token>' \
178
+ -H 'X-Spree-Token: <order_token>'
179
+ ```
180
+
181
+
182
+ ## Authentication Summary
183
+
184
+ | Method | Header | Use Case |
185
+ |--------|--------|----------|
186
+ | API Key | `X-Spree-Api-Key: spree_pk_xxx` | All requests (required) |
187
+ | JWT Token | `Authorization: Bearer <token>` | Authenticated customer actions |
188
+ | Order Token | `X-Spree-Token: <token>` | Guest cart and checkout |
@@ -0,0 +1,277 @@
1
+ ---
2
+ title: Errors
3
+ description: Error response format, error codes, and handling strategies
4
+ ---
5
+
6
+ The Store API uses a consistent, Stripe-style error format across all endpoints. Every error response includes a machine-readable `code` and a human-readable `message`.
7
+
8
+ ## Error Response Format
9
+
10
+ All errors return a JSON object with a single `error` key:
11
+
12
+ ```json
13
+ {
14
+ "error": {
15
+ "code": "record_not_found",
16
+ "message": "Product not found"
17
+ }
18
+ }
19
+ ```
20
+
21
+ Validation errors include an additional `details` field with per-field error messages:
22
+
23
+ ```json
24
+ {
25
+ "error": {
26
+ "code": "validation_error",
27
+ "message": "Name can't be blank and Email is invalid",
28
+ "details": {
29
+ "name": ["can't be blank"],
30
+ "email": ["is invalid"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Schema
37
+
38
+ | Field | Type | Description |
39
+ |-------|------|-------------|
40
+ | `error.code` | `string` | Machine-readable error code (see table below) |
41
+ | `error.message` | `string` | Human-readable description of the error |
42
+ | `error.details` | `object` | Field-specific validation errors. Each key is a field name, each value is an array of error strings. Only present for validation errors. |
43
+
44
+ ## HTTP Status Codes
45
+
46
+ | Status | Meaning | When |
47
+ |--------|---------|------|
48
+ | `400` | Bad Request | Missing required parameters, malformed JSON, invalid arguments |
49
+ | `401` | Unauthorized | Missing or invalid API key, expired JWT token |
50
+ | `403` | Forbidden | Authenticated but not authorized for this resource |
51
+ | `404` | Not Found | Resource doesn't exist or isn't accessible |
52
+ | `409` | Conflict | Resource was modified by another request (concurrent update) |
53
+ | `422` | Unprocessable Content | Validation failed, invalid state transition, payment error |
54
+ | `429` | Too Many Requests | Rate limit exceeded (see [Rate Limiting](/api-reference/store-api/rate-limitting)) |
55
+
56
+ ## Error Codes
57
+
58
+ ### Authentication & Authorization
59
+
60
+ | Code | Status | Description |
61
+ |------|--------|-------------|
62
+ | `authentication_required` | 401 | Request requires a valid JWT token |
63
+ | `authentication_failed` | 401 | Email or password is incorrect |
64
+ | `invalid_token` | 401 | API key or JWT token is invalid or expired |
65
+ | `invalid_provider` | 400 | OAuth provider not recognized |
66
+ | `access_denied` | 403 | User doesn't have permission for this action |
67
+
68
+ ### Resource Errors
69
+
70
+ | Code | Status | Description |
71
+ |------|--------|-------------|
72
+ | `record_not_found` | 404 | Resource doesn't exist or isn't accessible in the current store |
73
+ | `resource_invalid` | 422 | Resource couldn't be saved |
74
+
75
+ ### Validation Errors
76
+
77
+ | Code | Status | Description |
78
+ |------|--------|-------------|
79
+ | `validation_error` | 422 | Model validation failed. Check `details` for field-specific messages. |
80
+ | `parameter_missing` | 400 | A required parameter is missing |
81
+ | `parameter_invalid` | 400 | A parameter has an invalid value |
82
+
83
+ ### Cart Errors
84
+
85
+ | Code | Status | Description |
86
+ |------|--------|-------------|
87
+ | `cart_not_found` | 404 | Cart doesn't exist or doesn't belong to the current user/guest |
88
+ | `cart_cannot_complete` | 422 | Cart cannot be completed (e.g., missing payment or address) |
89
+ | `cart_cannot_transition` | 422 | Internal state machine transition failed (e.g., completing a cart that isn't ready) |
90
+ | `cart_empty` | 422 | Cart has no line items |
91
+ | `cart_invalid_state` | 422 | Cart is in an invalid state for this operation |
92
+ | `cart_already_updated` | 409 | Cart was modified by another request (optimistic locking conflict) |
93
+
94
+ ### Order Errors
95
+
96
+ | Code | Status | Description |
97
+ |------|--------|-------------|
98
+ | `order_not_found` | 404 | Completed order doesn't exist or doesn't belong to the current user/guest |
99
+
100
+ ### Line Item & Stock Errors
101
+
102
+ | Code | Status | Description |
103
+ |------|--------|-------------|
104
+ | `line_item_not_found` | 404 | Line item doesn't exist in this order |
105
+ | `variant_not_found` | 404 | Variant doesn't exist or isn't available |
106
+ | `insufficient_stock` | 422 | Not enough stock to fulfill the requested quantity |
107
+ | `invalid_quantity` | 422 | Quantity must be a positive integer |
108
+
109
+ ### Payment Errors
110
+
111
+ | Code | Status | Description |
112
+ |------|--------|-------------|
113
+ | `payment_failed` | 422 | Payment was declined or couldn't be processed |
114
+ | `payment_processing_error` | 422 | Error communicating with the payment gateway |
115
+ | `gateway_error` | 422 | Payment gateway returned an error |
116
+
117
+ ### Digital Download Errors
118
+
119
+ | Code | Status | Description |
120
+ |------|--------|-------------|
121
+ | `attachment_missing` | 403 | File attachment not found for this digital product |
122
+ | `download_unauthorized` | 403 | User is not authorized to download this file |
123
+ | `digital_link_expired` | 403 | Download link has expired |
124
+ | `download_limit_exceeded` | 403 | Maximum number of downloads reached |
125
+
126
+ ### Concurrency & Idempotency Errors
127
+
128
+ | Code | Status | Description |
129
+ |------|--------|-------------|
130
+ | `order_already_updated` | 409 | Order was modified by another concurrent request. Retry with fresh data. |
131
+ | `idempotency_key_reused` | 422 | Idempotency key was already used with different request parameters. See [Idempotency](/api-reference/store-api/idempotency). |
132
+
133
+ ### Other Errors
134
+
135
+ | Code | Status | Description |
136
+ |------|--------|-------------|
137
+ | `rate_limit_exceeded` | 429 | Too many requests. Retry after the `Retry-After` header value. |
138
+ | `request_too_large` | 413 | Request body exceeds the size limit |
139
+ | `processing_error` | 422 | Generic server-side processing error |
140
+ | `invalid_request` | 400 | Request is malformed (e.g., invalid JSON body) |
141
+
142
+ ## Examples
143
+
144
+ ### Not Found
145
+
146
+ ```bash
147
+ curl https://api.mystore.com/api/v3/store/products/prod_nonexistent \
148
+ -H "Authorization: Bearer spree_pk_xxx"
149
+ ```
150
+
151
+ ```json 404
152
+ {
153
+ "error": {
154
+ "code": "record_not_found",
155
+ "message": "Product not found"
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### Validation Error
161
+
162
+ ```bash
163
+ curl -X POST https://api.mystore.com/api/v3/store/account/addresses \
164
+ -H "Authorization: Bearer <jwt_token>" \
165
+ -H "Content-Type: application/json" \
166
+ -d '{}'
167
+ ```
168
+
169
+ ```json 422
170
+ {
171
+ "error": {
172
+ "code": "validation_error",
173
+ "message": "First name can't be blank, Address can't be blank, City can't be blank, and Country can't be blank",
174
+ "details": {
175
+ "firstname": ["can't be blank"],
176
+ "address1": ["can't be blank"],
177
+ "city": ["can't be blank"],
178
+ "country": ["can't be blank"]
179
+ }
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Insufficient Stock
185
+
186
+ ```bash
187
+ curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/items \
188
+ -H "Authorization: Bearer spree_pk_xxx" \
189
+ -H "X-Spree-Token: abc123" \
190
+ -H "Content-Type: application/json" \
191
+ -d '{"variant_id": "var_xxx", "quantity": 999}'
192
+ ```
193
+
194
+ ```json 422
195
+ {
196
+ "error": {
197
+ "code": "insufficient_stock",
198
+ "message": "Quantity selected exceeds available stock"
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### Invalid State Transition
204
+
205
+ ```bash
206
+ curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/complete \
207
+ -H "Authorization: Bearer spree_pk_xxx"
208
+ ```
209
+
210
+ ```json 422
211
+ {
212
+ "error": {
213
+ "code": "cart_cannot_transition",
214
+ "message": "Cannot transition cart to complete — ensure address, shipping, and payment are set"
215
+ }
216
+ }
217
+ ```
218
+
219
+ ## Handling Errors in the SDK
220
+
221
+ The `@spree/sdk` package throws a `SpreeError` for all non-2xx responses:
222
+
223
+ ```typescript
224
+ import { SpreeError } from '@spree/sdk';
225
+
226
+ try {
227
+ const product = await client.products.get('nonexistent');
228
+ } catch (error) {
229
+ if (error instanceof SpreeError) {
230
+ console.log(error.code); // 'record_not_found'
231
+ console.log(error.message); // 'Product not found'
232
+ console.log(error.status); // 404
233
+ console.log(error.details); // undefined (or field errors for validation)
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### Common Patterns
239
+
240
+ Handle specific error codes:
241
+
242
+ ```typescript
243
+ try {
244
+ await client.carts.items.create(cartId, {
245
+ variant_id: variantId,
246
+ quantity: 1,
247
+ });
248
+ } catch (error) {
249
+ if (error instanceof SpreeError) {
250
+ switch (error.code) {
251
+ case 'insufficient_stock':
252
+ showNotification('This item is out of stock');
253
+ break;
254
+ case 'variant_not_found':
255
+ showNotification('This product is no longer available');
256
+ break;
257
+ default:
258
+ showNotification(error.message);
259
+ }
260
+ }
261
+ }
262
+ ```
263
+
264
+ Display validation errors per field:
265
+
266
+ ```typescript
267
+ try {
268
+ await client.customer.addresses.create(addressData);
269
+ } catch (error) {
270
+ if (error instanceof SpreeError && error.details) {
271
+ // error.details = { city: ["can't be blank"], zipcode: ["is invalid"] }
272
+ for (const [field, messages] of Object.entries(error.details)) {
273
+ setFieldError(field, messages.join(', '));
274
+ }
275
+ }
276
+ }
277
+ ```
@@ -0,0 +1,129 @@
1
+ ---
2
+ title: "Idempotency"
3
+ sidebarTitle: "Idempotency"
4
+ description: "Prevent duplicate operations with idempotency keys"
5
+ ---
6
+
7
+ The Store API supports idempotency keys on mutating endpoints. This lets you safely retry requests without risking duplicate side effects — for example, adding an item to cart twice or creating duplicate payments due to network timeouts.
8
+
9
+ ## How It Works
10
+
11
+ Include an `Idempotency-Key` header with a unique value (e.g., a UUID) on any supported request. The API will:
12
+
13
+ 1. **First request** — process normally and cache the response for 24 hours
14
+ 2. **Duplicate request** (same key, same parameters) — return the cached response without re-executing the operation
15
+ 3. **Key reuse with different parameters** — return a `422` error to prevent misuse
16
+
17
+ ```bash
18
+ curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/items \
19
+ -H "Authorization: Bearer spree_pk_xxx" \
20
+ -H "X-Spree-Token: abc123" \
21
+ -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
22
+ -H "Content-Type: application/json" \
23
+ -d '{"variant_id": "variant_xxx", "quantity": 1}'
24
+ ```
25
+
26
+ If the client retries this exact request with the same idempotency key, the API returns the original response with an `Idempotent-Replayed: true` header — without adding the item again.
27
+
28
+ ## Supported Endpoints
29
+
30
+ | Endpoint | Actions |
31
+ |----------|---------|
32
+ | `POST /carts` | Cart creation |
33
+ | `PATCH /carts/:id` | Cart updates |
34
+ | `POST /carts/:id/complete` | Order completion |
35
+ | `POST /carts/:id/items` | Adding items to cart |
36
+ | `POST /carts/:id/payment_sessions` | Payment session creation |
37
+ | `PATCH /carts/:id/payment_sessions/:id/complete` | Payment completion |
38
+ | `POST /carts/:id/coupon_codes` | Applying coupon codes |
39
+ | `POST /carts/:id/store_credits` | Applying store credits |
40
+
41
+ Idempotency keys are ignored on `GET`, `DELETE`, and other non-supported actions.
42
+
43
+ ## Key Requirements
44
+
45
+ - Must be a string of **255 characters or less**
46
+ - Should be unique per distinct operation (UUIDs are recommended)
47
+ - Keys are scoped per API key — different API keys can use the same idempotency key without conflict
48
+ - Cached responses expire after **24 hours**
49
+
50
+ ## Response Headers
51
+
52
+ When a cached response is replayed, the API sets:
53
+
54
+ ```
55
+ Idempotent-Replayed: true
56
+ ```
57
+
58
+ You can use this header to detect replayed responses in your application or logging.
59
+
60
+ ## Error Handling
61
+
62
+ ### Key Reuse with Different Parameters
63
+
64
+ If you send a request with an idempotency key that was previously used with different request parameters (different body, different path), the API returns a `422` error:
65
+
66
+ ```json
67
+ {
68
+ "error": {
69
+ "code": "idempotency_key_reused",
70
+ "message": "This Idempotency-Key has already been used with different request parameters."
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### Key Too Long
76
+
77
+ Keys longer than 255 characters return a `400` error:
78
+
79
+ ```json
80
+ {
81
+ "error": {
82
+ "code": "invalid_request",
83
+ "message": "Idempotency-Key must be 255 characters or less."
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Server Errors
89
+
90
+ `5xx` responses are **not cached**. If the server fails to process your request, you can safely retry with the same idempotency key and the request will be re-executed.
91
+
92
+ ## SDK Usage
93
+
94
+ The Spree SDK supports idempotency keys via the `idempotencyKey` option:
95
+
96
+ ```typescript
97
+ import { createClient } from '@spree/sdk'
98
+
99
+ const client = createClient({
100
+ baseUrl: 'http://localhost:3000',
101
+ publishableKey: 'spree_pk_xxx',
102
+ })
103
+
104
+ // Add item to cart with idempotency key
105
+ await client.carts.items.create(cartId, {
106
+ variant_id: 'variant_xxx',
107
+ quantity: 1,
108
+ }, {
109
+ idempotencyKey: '550e8400-e29b-41d4-a716-446655440000',
110
+ })
111
+ ```
112
+
113
+ ## Best Practices
114
+
115
+ - **Generate a new key for each distinct operation.** Use a UUID (v4) or another random identifier. Never reuse keys across different operations.
116
+ - **Store the key before sending the request.** If the request fails due to a network error, retry with the same key to ensure the operation is only performed once.
117
+ - **Don't use idempotency keys for GET requests.** GET requests are naturally idempotent and the API ignores the header on read-only endpoints.
118
+ - **Let keys expire naturally.** Cached responses expire after 24 hours. Don't rely on idempotency keys for longer-term deduplication.
119
+
120
+ ## Relationship with Optimistic Locking
121
+
122
+ The Store API also uses [optimistic locking](/api-reference/store-api/introduction) via the `state_lock_version` field on orders. These mechanisms complement each other:
123
+
124
+ | Mechanism | Prevents | Scope |
125
+ |-----------|----------|-------|
126
+ | **Idempotency keys** | Duplicate operations from retries | Per-request deduplication |
127
+ | **Optimistic locking** (`state_lock_version`) | Concurrent modifications | Conflict detection between clients |
128
+
129
+ Use both together for robust checkout flows: idempotency keys protect against network retries, while `state_lock_version` detects when another client has modified the order.