@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.
- package/README.md +54 -0
- package/dist/api-reference/platform/authentication.md +38 -0
- package/dist/api-reference/store-api/authentication.md +188 -0
- package/dist/api-reference/store-api/errors.md +277 -0
- package/dist/api-reference/store-api/idempotency.md +129 -0
- package/dist/api-reference/store-api/introduction.md +34 -0
- package/dist/api-reference/store-api/localization.md +279 -0
- package/dist/api-reference/store-api/metadata.md +160 -0
- package/dist/api-reference/store-api/monetary-amounts.md +65 -0
- package/dist/api-reference/store-api/querying.md +399 -0
- package/dist/api-reference/store-api/rate-limitting.md +103 -0
- package/dist/api-reference/store-api/relations.md +185 -0
- package/dist/api-reference/storefront/authentication.md +88 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
- package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
- package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
- package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
- package/dist/api-reference/v2/introduction.md +22 -0
- package/dist/api-reference/v2/pagination.md +37 -0
- package/dist/api-reference/webhooks-events.md +883 -0
- package/dist/developer/admin/admin.md +205 -0
- package/dist/developer/admin/authentication.md +59 -0
- package/dist/developer/admin/components.md +711 -0
- package/dist/developer/admin/custom-css.md +243 -0
- package/dist/developer/admin/custom-javascript.md +116 -0
- package/dist/developer/admin/extending-ui.md +1964 -0
- package/dist/developer/admin/form-builder.md +444 -0
- package/dist/developer/admin/helper-methods.md +531 -0
- package/dist/developer/admin/navigation.md +805 -0
- package/dist/developer/admin/tables.md +491 -0
- package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
- package/dist/developer/cli/quickstart.md +137 -0
- package/dist/developer/contributing/creating-an-extension.md +258 -0
- package/dist/developer/contributing/developing-spree.md +339 -0
- package/dist/developer/contributing/quickstart.md +32 -0
- package/dist/developer/contributing/updating-extensions.md +67 -0
- package/dist/developer/core-concepts/addresses.md +265 -0
- package/dist/developer/core-concepts/adjustments.md +107 -0
- package/dist/developer/core-concepts/architecture.md +177 -0
- package/dist/developer/core-concepts/calculators.md +323 -0
- package/dist/developer/core-concepts/customers.md +230 -0
- package/dist/developer/core-concepts/events.md +624 -0
- package/dist/developer/core-concepts/imports-exports.md +698 -0
- package/dist/developer/core-concepts/inventory.md +191 -0
- package/dist/developer/core-concepts/markets.md +250 -0
- package/dist/developer/core-concepts/media.md +167 -0
- package/dist/developer/core-concepts/metafields.md +187 -0
- package/dist/developer/core-concepts/orders.md +328 -0
- package/dist/developer/core-concepts/payments.md +710 -0
- package/dist/developer/core-concepts/pricing.md +163 -0
- package/dist/developer/core-concepts/products.md +360 -0
- package/dist/developer/core-concepts/promotions.md +322 -0
- package/dist/developer/core-concepts/reports.md +206 -0
- package/dist/developer/core-concepts/search-filtering.md +237 -0
- package/dist/developer/core-concepts/shipments.md +212 -0
- package/dist/developer/core-concepts/slugs.md +111 -0
- package/dist/developer/core-concepts/staff-roles.md +123 -0
- package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
- package/dist/developer/core-concepts/stores.md +117 -0
- package/dist/developer/core-concepts/taxes.md +135 -0
- package/dist/developer/core-concepts/translations.md +120 -0
- package/dist/developer/core-concepts/users.md +299 -0
- package/dist/developer/core-concepts/webhooks.md +378 -0
- package/dist/developer/create-spree-app/quickstart.md +158 -0
- package/dist/developer/customization/api.md +93 -0
- package/dist/developer/customization/authentication.md +88 -0
- package/dist/developer/customization/checkout.md +204 -0
- package/dist/developer/customization/configuration.md +55 -0
- package/dist/developer/customization/decorators.md +523 -0
- package/dist/developer/customization/dependencies.md +232 -0
- package/dist/developer/customization/emails.md +21 -0
- package/dist/developer/customization/extensions.md +92 -0
- package/dist/developer/customization/metadata.md +236 -0
- package/dist/developer/customization/model-preferences.md +130 -0
- package/dist/developer/customization/permissions.md +265 -0
- package/dist/developer/customization/quickstart.md +229 -0
- package/dist/developer/customization/routes.md +24 -0
- package/dist/developer/customization/v4/admin-panel.md +78 -0
- package/dist/developer/customization/v4/authentication.md +210 -0
- package/dist/developer/customization/v4/checkout.md +212 -0
- package/dist/developer/customization/v4/deface.md +251 -0
- package/dist/developer/customization/v4/images.md +86 -0
- package/dist/developer/customization/v4/storefront.md +450 -0
- package/dist/developer/deployment/assets.md +87 -0
- package/dist/developer/deployment/aws.md +335 -0
- package/dist/developer/deployment/caching.md +27 -0
- package/dist/developer/deployment/cdn.md +39 -0
- package/dist/developer/deployment/database.md +155 -0
- package/dist/developer/deployment/docker.md +128 -0
- package/dist/developer/deployment/emails.md +77 -0
- package/dist/developer/deployment/environment_variables.md +111 -0
- package/dist/developer/deployment/heroku.md +51 -0
- package/dist/developer/deployment/render.md +95 -0
- package/dist/developer/getting-started/quickstart.md +82 -0
- package/dist/developer/how-to/custom-payment-method.md +374 -0
- package/dist/developer/how-to/custom-promotion.md +373 -0
- package/dist/developer/how-to/custom-report.md +387 -0
- package/dist/developer/how-to/custom-search-provider.md +230 -0
- package/dist/developer/multi-store/quickstart.md +71 -0
- package/dist/developer/multi-store/setup.md +38 -0
- package/dist/developer/multi-tenant/configuration.md +41 -0
- package/dist/developer/multi-tenant/core-concepts.md +75 -0
- package/dist/developer/multi-tenant/installation.md +96 -0
- package/dist/developer/multi-tenant/quickstart.md +20 -0
- package/dist/developer/multi-vendor/installation.md +45 -0
- package/dist/developer/multi-vendor/quickstart.md +17 -0
- package/dist/developer/sdk/admin/quickstart.md +22 -0
- package/dist/developer/sdk/authentication.md +89 -0
- package/dist/developer/sdk/configuration.md +225 -0
- package/dist/developer/sdk/quickstart.md +82 -0
- package/dist/developer/sdk/store/account.md +67 -0
- package/dist/developer/sdk/store/cart-checkout.md +140 -0
- package/dist/developer/sdk/store/markets.md +151 -0
- package/dist/developer/sdk/store/payments.md +96 -0
- package/dist/developer/sdk/store/products.md +149 -0
- package/dist/developer/sdk/store/wishlists.md +52 -0
- package/dist/developer/security/pci_compliance.md +15 -0
- package/dist/developer/security/security_policy.md +68 -0
- package/dist/developer/storefront/blocks.md +285 -0
- package/dist/developer/storefront/custom-css.md +260 -0
- package/dist/developer/storefront/custom-javascript.md +166 -0
- package/dist/developer/storefront/helper-methods.md +1288 -0
- package/dist/developer/storefront/links.md +298 -0
- package/dist/developer/storefront/nextjs/architecture.md +150 -0
- package/dist/developer/storefront/nextjs/customization.md +141 -0
- package/dist/developer/storefront/nextjs/deployment.md +180 -0
- package/dist/developer/storefront/nextjs/quickstart.md +92 -0
- package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
- package/dist/developer/storefront/pages.md +163 -0
- package/dist/developer/storefront/sections.md +569 -0
- package/dist/developer/storefront/storefront.md +56 -0
- package/dist/developer/storefront/themes.md +161 -0
- package/dist/developer/tutorial/admin.md +134 -0
- package/dist/developer/tutorial/extending-models.md +380 -0
- package/dist/developer/tutorial/file-uploads.md +121 -0
- package/dist/developer/tutorial/introduction.md +33 -0
- package/dist/developer/tutorial/model.md +41 -0
- package/dist/developer/tutorial/page-builder.md +487 -0
- package/dist/developer/tutorial/rich-text.md +73 -0
- package/dist/developer/tutorial/seo.md +332 -0
- package/dist/developer/tutorial/storefront.md +352 -0
- package/dist/developer/tutorial/testing.md +558 -0
- package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
- package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
- package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
- package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
- package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
- package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
- package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
- package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
- package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
- package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
- package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
- package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
- package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
- package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
- package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
- package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
- package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
- package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
- package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
- package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
- package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
- package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
- package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
- package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
- package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
- package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
- package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
- package/dist/developer/upgrades/quickstart.md +36 -0
- package/dist/integrations/analytics/google-analytics.md +64 -0
- package/dist/integrations/analytics/google-tag-manager.md +78 -0
- package/dist/integrations/integrations.md +39 -0
- package/dist/integrations/marketing/klaviyo.md +99 -0
- package/dist/integrations/payments/adyen.md +90 -0
- package/dist/integrations/payments/paypal.md +41 -0
- package/dist/integrations/payments/razorpay.md +45 -0
- package/dist/integrations/payments/stripe.md +109 -0
- package/dist/integrations/search/meilisearch.md +236 -0
- package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
- package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
- package/package.json +27 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pricing
|
|
3
|
+
description: Prices, Price Lists, Price Rules, and the Pricing Context — Spree's flexible pricing engine for regional, wholesale, volume, and market-based pricing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
Spree's pricing system supports both simple single-currency pricing and advanced multi-currency, rule-based pricing through Price Lists. Every [Variant](/developer/core-concepts/products#variants) can have multiple prices — a base price per currency, plus additional prices from Price Lists that apply conditionally based on rules like geography, customer segment, or quantity.
|
|
9
|
+
|
|
10
|
+
## Prices
|
|
11
|
+
|
|
12
|
+
Each variant has one or more `Price` records — one per currency. The API automatically returns the correct price based on the current currency and [Market](/developer/core-concepts/markets) context.
|
|
13
|
+
|
|
14
|
+
| Attribute | Description | Example |
|
|
15
|
+
|-----------|-------------|---------|
|
|
16
|
+
| `amount` | Current selling price | `99.90` |
|
|
17
|
+
| `compare_at_amount` | Original/compare price for strikethrough display | `129.90` |
|
|
18
|
+
| `currency` | ISO 4217 currency code | `USD` |
|
|
19
|
+
|
|
20
|
+
> **WARNING:** If a product doesn't have a price in the selected currency, it won't appear in Store API responses by default.
|
|
21
|
+
|
|
22
|
+
### Fetching Prices via API
|
|
23
|
+
|
|
24
|
+
The Store API returns the resolved price (including Price List rules) for the current currency and market context:
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
```typescript SDK
|
|
28
|
+
// Product price is included by default
|
|
29
|
+
const product = await client.products.get('spree-tote')
|
|
30
|
+
console.log(product.price) // "15.99" — resolved for current currency
|
|
31
|
+
console.log(product.original_price) // "19.99" — compare-at price (if set)
|
|
32
|
+
|
|
33
|
+
// Each variant has its own price
|
|
34
|
+
const detailed = await client.products.get('spree-tote', {
|
|
35
|
+
expand: ['variants'],
|
|
36
|
+
})
|
|
37
|
+
detailed.variants?.forEach(variant => {
|
|
38
|
+
console.log(variant.price) // "15.99"
|
|
39
|
+
console.log(variant.original_price) // "19.99"
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```bash cURL
|
|
44
|
+
# Product price is always in the response
|
|
45
|
+
curl 'https://api.mystore.com/api/v3/store/products/spree-tote' \
|
|
46
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
47
|
+
|
|
48
|
+
# With variants
|
|
49
|
+
curl 'https://api.mystore.com/api/v3/store/products/spree-tote?expand=variants' \
|
|
50
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Price Lists
|
|
55
|
+
|
|
56
|
+
Price Lists allow you to create different pricing strategies based on various conditions. This enables advanced pricing scenarios like:
|
|
57
|
+
|
|
58
|
+
- **Market-based pricing** — different prices for different [Markets](/developer/core-concepts/markets) (e.g., North America vs Europe)
|
|
59
|
+
- **Regional pricing** — different prices for different geographic zones
|
|
60
|
+
- **Wholesale/B2B pricing** — special prices for business customers
|
|
61
|
+
- **Volume discounts** — tiered pricing based on quantity purchased
|
|
62
|
+
- **Promotional pricing** — time-limited special offers
|
|
63
|
+
- **VIP customer pricing** — exclusive prices for specific customers
|
|
64
|
+
|
|
65
|
+
### How Price Lists Work
|
|
66
|
+
|
|
67
|
+
When a customer views a product, Spree's pricing resolver determines which price to use:
|
|
68
|
+
|
|
69
|
+
1. **Price List priority** — Price Lists are ordered by position; higher priority lists are checked first
|
|
70
|
+
2. **Status** — only `active` or `scheduled` Price Lists are considered
|
|
71
|
+
3. **Date range** — the current time must fall within `starts_at` and `ends_at` (if set)
|
|
72
|
+
4. **Price Rules** — all configured rules must match (or any, depending on `match_policy`)
|
|
73
|
+
|
|
74
|
+
If no applicable Price List is found, the base price is used.
|
|
75
|
+
|
|
76
|
+
### Price List Attributes
|
|
77
|
+
|
|
78
|
+
| Attribute | Description |
|
|
79
|
+
|-----------|-------------|
|
|
80
|
+
| `name` | Human-readable name for the Price List |
|
|
81
|
+
| `status` | `draft`, `active`, `scheduled`, or `inactive` |
|
|
82
|
+
| `starts_at` | Optional start date when the Price List becomes applicable |
|
|
83
|
+
| `ends_at` | Optional end date when the Price List stops being applicable |
|
|
84
|
+
| `match_policy` | How rules are evaluated: `all` (every rule must match) or `any` (at least one) |
|
|
85
|
+
| `position` | Priority order (lower numbers = higher priority) |
|
|
86
|
+
|
|
87
|
+
## Price Rules
|
|
88
|
+
|
|
89
|
+
Price Rules define conditions that must be met for a Price List to apply. Spree includes five built-in rule types:
|
|
90
|
+
|
|
91
|
+
| Rule | Description | Use Case |
|
|
92
|
+
|------|-------------|----------|
|
|
93
|
+
| **Market Rule** | Matches based on the current [Market](/developer/core-concepts/markets) | Regional pricing across markets |
|
|
94
|
+
| **Zone Rule** | Matches based on the customer's geographic zone | Country or state-level pricing |
|
|
95
|
+
| **User Rule** | Matches specific customer accounts | VIP customers, wholesale accounts |
|
|
96
|
+
| **Customer Group Rule** | Matches members of customer groups | Loyalty tiers, membership pricing |
|
|
97
|
+
| **Volume Rule** | Matches based on quantity purchased | Bulk discounts, tiered pricing |
|
|
98
|
+
|
|
99
|
+
### Market Rule
|
|
100
|
+
|
|
101
|
+
The recommended approach for regional pricing when using Markets. Applies the Price List when the customer is in one of the specified markets.
|
|
102
|
+
|
|
103
|
+
**Example:** Price a product at $29.99 in North America and €24.99 in Europe, rather than relying on exchange rate conversion.
|
|
104
|
+
|
|
105
|
+
### Zone Rule
|
|
106
|
+
|
|
107
|
+
Applies based on the customer's geographic zone. Useful for regional or country-specific pricing when not using Markets.
|
|
108
|
+
|
|
109
|
+
### User Rule
|
|
110
|
+
|
|
111
|
+
Limits the Price List to specific customer accounts. Useful for VIP customers, employee pricing, or wholesale accounts.
|
|
112
|
+
|
|
113
|
+
### Customer Group Rule
|
|
114
|
+
|
|
115
|
+
Applies to members of specific customer groups. Useful for wholesale tiers, loyalty programs, or membership-based pricing.
|
|
116
|
+
|
|
117
|
+
### Volume Rule
|
|
118
|
+
|
|
119
|
+
Applies based on quantity purchased. Supports `min_quantity` and `max_quantity` to create tiered pricing:
|
|
120
|
+
|
|
121
|
+
| Tier | Quantity | Price |
|
|
122
|
+
|------|----------|-------|
|
|
123
|
+
| Base | 1–9 | $10.00 |
|
|
124
|
+
| Bulk Tier 1 | 10–49 | $8.50 |
|
|
125
|
+
| Bulk Tier 2 | 50+ | $7.00 |
|
|
126
|
+
|
|
127
|
+
> **INFO:** Custom Price Rules can be created for specialized pricing logic. See the [Customization Quickstart](/developer/customization/quickstart) for details.
|
|
128
|
+
|
|
129
|
+
## Pricing Context
|
|
130
|
+
|
|
131
|
+
When resolving prices, Spree considers the full context of the request:
|
|
132
|
+
|
|
133
|
+
| Context | Source | Description |
|
|
134
|
+
|---------|--------|-------------|
|
|
135
|
+
| Currency | Market or request header | The currency to price in |
|
|
136
|
+
| Market | Customer's country | The [Market](/developer/core-concepts/markets) for market-based rules |
|
|
137
|
+
| Zone | Customer's address | The geographic zone for zone-based rules |
|
|
138
|
+
| Customer | JWT authentication | The logged-in customer for user-based rules |
|
|
139
|
+
| Quantity | Cart line item | The quantity for volume-based rules |
|
|
140
|
+
| Date | Current time | For time-based Price List scheduling |
|
|
141
|
+
|
|
142
|
+
The Store API automatically builds this context from the request headers (`X-Spree-Currency`, `X-Spree-Country`) and authentication state. You don't need to construct it manually — just make API requests and the correct price is resolved.
|
|
143
|
+
|
|
144
|
+
> **INFO:** Price resolution results are cached for 15 minutes. Different context combinations are cached separately.
|
|
145
|
+
|
|
146
|
+
## Time-Based Pricing
|
|
147
|
+
|
|
148
|
+
Price Lists support scheduling through `starts_at` and `ends_at` attributes. Scheduled Price Lists automatically become applicable when the current time falls within their date range — no manual activation needed.
|
|
149
|
+
|
|
150
|
+
**Example:** A Black Friday sale Price List with `starts_at: 2025-11-28 00:00` and `ends_at: 2025-11-28 23:59` will automatically activate and deactivate.
|
|
151
|
+
|
|
152
|
+
## Managing Price Lists
|
|
153
|
+
|
|
154
|
+
Price Lists are managed in the Admin Panel under **Products → Price Lists**, or via the Admin API.
|
|
155
|
+
|
|
156
|
+
Each Price List contains prices for specific variants and currencies. Products can be added to a Price List, and individual variant prices set within it.
|
|
157
|
+
|
|
158
|
+
## Related Documentation
|
|
159
|
+
|
|
160
|
+
- [Products](/developer/core-concepts/products) — Products, Variants, and base prices
|
|
161
|
+
- [Markets](/developer/core-concepts/markets) — Geographic regions with currency and locale
|
|
162
|
+
- [Taxes](/developer/core-concepts/taxes) — Tax categories, tax rates, and zones
|
|
163
|
+
- [Promotions](/developer/core-concepts/promotions) — Discount-based pricing via promotion rules
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Products
|
|
3
|
+
description: Products, variants, option types, images, prices, and categories
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
A product represents something you sell. Each product has one or more **variants** — the actual purchasable items with their own SKU, price, and inventory. For example, a "T-Shirt" product might have variants for each size and color combination.
|
|
9
|
+
|
|
10
|
+
Products are organized into **categories** — a flexible hierarchy for grouping products. Categories can be filtered, sorted, and searched via the Store API.
|
|
11
|
+
|
|
12
|
+
> **INFO:** Product names, descriptions, slugs, and SEO fields are [translatable](/developer/core-concepts/translations#resource-translations).
|
|
13
|
+
|
|
14
|
+
```mermaid
|
|
15
|
+
erDiagram
|
|
16
|
+
Product ||--o{ Variant : "has many"
|
|
17
|
+
Product }o--o{ OptionType : "has many"
|
|
18
|
+
Product ||--o{ Classification : "has many"
|
|
19
|
+
Variant ||--o{ Price : "has many"
|
|
20
|
+
Variant ||--o{ StockItem : "has many"
|
|
21
|
+
Variant ||--o{ Image : "has many"
|
|
22
|
+
Variant }o--o{ OptionValue : "has many"
|
|
23
|
+
OptionType ||--o{ OptionValue : "has many"
|
|
24
|
+
Taxon ||--o{ Classification : "has many"
|
|
25
|
+
Taxonomy ||--o{ Taxon : "has many"
|
|
26
|
+
|
|
27
|
+
Product {
|
|
28
|
+
string name
|
|
29
|
+
string slug
|
|
30
|
+
string status
|
|
31
|
+
text description
|
|
32
|
+
datetime available_on
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Variant {
|
|
36
|
+
string sku
|
|
37
|
+
boolean is_master
|
|
38
|
+
decimal weight
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Price {
|
|
42
|
+
decimal amount
|
|
43
|
+
decimal compare_at_amount
|
|
44
|
+
string currency
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
OptionType {
|
|
48
|
+
string name
|
|
49
|
+
string presentation
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
OptionValue {
|
|
53
|
+
string name
|
|
54
|
+
string presentation
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Product Attributes
|
|
59
|
+
|
|
60
|
+
| Attribute | Description | Translatable |
|
|
61
|
+
|-----------|-------------|:---:|
|
|
62
|
+
| `name` | Product name | Yes |
|
|
63
|
+
| `description` | Full product description | Yes |
|
|
64
|
+
| `slug` | URL-friendly identifier (e.g., `spree-tote`) | Yes |
|
|
65
|
+
| `status` | `draft`, `active`, or `archived` | No |
|
|
66
|
+
| `available_on` | Date the product becomes available for sale | No |
|
|
67
|
+
| `discontinue_on` | Date the product is no longer available | No |
|
|
68
|
+
| `meta_title` | Custom SEO title | Yes |
|
|
69
|
+
| `meta_description` | SEO description | Yes |
|
|
70
|
+
| `meta_keywords` | SEO keywords | Yes |
|
|
71
|
+
| `purchasable` | Whether the product can be added to cart | No |
|
|
72
|
+
| `in_stock` | Whether any variant has stock available | No |
|
|
73
|
+
| `price` | Default variant's price in the current currency | No |
|
|
74
|
+
| `thumbnail_url` | URL to the product's first image — always returned, no expand needed | No |
|
|
75
|
+
| `tags` | Array of tag strings for filtering | No |
|
|
76
|
+
|
|
77
|
+
## Listing Products
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
```typescript SDK
|
|
81
|
+
// List products with pagination
|
|
82
|
+
const { data: products, meta } = await client.products.list({
|
|
83
|
+
limit: 12,
|
|
84
|
+
page: 1,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Filter by price range and availability
|
|
88
|
+
const filtered = await client.products.list({
|
|
89
|
+
price_gte: 10,
|
|
90
|
+
price_lte: 50,
|
|
91
|
+
in_stock: true,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// Search by keyword
|
|
95
|
+
const results = await client.products.list({
|
|
96
|
+
search: 'tote bag',
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Sort products
|
|
100
|
+
const sorted = await client.products.list({
|
|
101
|
+
sort: 'price_high_to_low', // or: price_low_to_high, newest, name_a_z, name_z_a
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```bash cURL
|
|
106
|
+
# List products
|
|
107
|
+
curl 'https://api.mystore.com/api/v3/store/products?limit=12&page=1' \
|
|
108
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
109
|
+
|
|
110
|
+
# Filter by price and stock
|
|
111
|
+
curl 'https://api.mystore.com/api/v3/store/products?q[price_gte]=10&q[price_lte]=50&q[in_stock]=true' \
|
|
112
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
113
|
+
|
|
114
|
+
# Search
|
|
115
|
+
curl 'https://api.mystore.com/api/v3/store/products?q[search]=tote+bag' \
|
|
116
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
See [Querying](/api-reference/store-api/querying) for the full list of filtering, sorting, and pagination options.
|
|
121
|
+
|
|
122
|
+
## Getting a Product
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
```typescript SDK
|
|
126
|
+
// Get by slug
|
|
127
|
+
const product = await client.products.get('spree-tote')
|
|
128
|
+
|
|
129
|
+
// Get with included relations
|
|
130
|
+
const detailed = await client.products.get('spree-tote', {
|
|
131
|
+
expand: ['variants', 'media', 'option_types', 'categories'],
|
|
132
|
+
})
|
|
133
|
+
// detailed.variants => [{ id: "var_xxx", sku: "TOTE-S-R", price: { amount: "15.99", currency: "USD" }, ... }]
|
|
134
|
+
// detailed.media => [{ id: "img_xxx", url: "https://cdn...", position: 1 }]
|
|
135
|
+
// detailed.option_types => [{ name: "size", presentation: "Size", option_values: [...] }]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```bash cURL
|
|
139
|
+
curl 'https://api.mystore.com/api/v3/store/products/spree-tote?expand=variants,media,option_types,categories' \
|
|
140
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
## Product Filters
|
|
145
|
+
|
|
146
|
+
Get available filter options for building a faceted search UI. Returns price ranges, option values, and categories with counts:
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
```typescript SDK
|
|
150
|
+
const filters = await client.products.filters()
|
|
151
|
+
// {
|
|
152
|
+
// option_types: [{ name: "size", option_values: [{ name: "Small", count: 12 }, ...] }],
|
|
153
|
+
// price_range: { min: 9.99, max: 199.99 },
|
|
154
|
+
// categories: [{ id: "ctg_xxx", name: "Clothing", count: 45 }],
|
|
155
|
+
// }
|
|
156
|
+
|
|
157
|
+
// Scoped to a specific category
|
|
158
|
+
const categoryFilters = await client.products.filters({
|
|
159
|
+
category_id: 'ctg_xxx',
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```bash cURL
|
|
164
|
+
curl 'https://api.mystore.com/api/v3/store/products/filters' \
|
|
165
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
166
|
+
|
|
167
|
+
# Scoped to a category
|
|
168
|
+
curl 'https://api.mystore.com/api/v3/store/products/filters?category_id=ctg_xxx' \
|
|
169
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
## Variants
|
|
174
|
+
|
|
175
|
+
Variants are the purchasable units of a product. Each variant has its own SKU, price, inventory, and images, and is defined by a unique combination of option values.
|
|
176
|
+
|
|
177
|
+
| Attribute | Description |
|
|
178
|
+
|-----------|-------------|
|
|
179
|
+
| `sku` | Unique stock keeping unit |
|
|
180
|
+
| `barcode` | Barcode (UPC, EAN, etc.) |
|
|
181
|
+
| `price` | Price in the current currency |
|
|
182
|
+
| `original_price` | Compare-at price for showing discounts |
|
|
183
|
+
| `weight`, `height`, `width`, `depth` | Dimensions for shipping calculations |
|
|
184
|
+
| `in_stock` | Whether stock is available |
|
|
185
|
+
| `backorderable` | Whether the variant can be ordered when out of stock |
|
|
186
|
+
| `option_values` | The option values that define this variant (e.g., Size: Small, Color: Red) |
|
|
187
|
+
|
|
188
|
+
### Master Variant
|
|
189
|
+
|
|
190
|
+
Every product has a **master variant** that holds default pricing and inventory. If a product has no option types (e.g., a book with no size/color), the master variant is the only purchasable variant.
|
|
191
|
+
|
|
192
|
+
### Regular Variants
|
|
193
|
+
|
|
194
|
+
When a product has option types, each unique combination of option values creates a variant. For example, a T-shirt with sizes (S, M, L) and colors (Red, Green) has 6 variants:
|
|
195
|
+
|
|
196
|
+
| SKU | Size | Color |
|
|
197
|
+
|-----|------|-------|
|
|
198
|
+
| `TEE-S-R` | Small | Red |
|
|
199
|
+
| `TEE-S-G` | Small | Green |
|
|
200
|
+
| `TEE-M-R` | Medium | Red |
|
|
201
|
+
| `TEE-M-G` | Medium | Green |
|
|
202
|
+
| `TEE-L-R` | Large | Red |
|
|
203
|
+
| `TEE-L-G` | Large | Green |
|
|
204
|
+
|
|
205
|
+
The product's `default_variant_id` points to the first non-master variant (or the master variant if none exist).
|
|
206
|
+
|
|
207
|
+
## Option Types and Option Values
|
|
208
|
+
|
|
209
|
+
Option types define the axes of variation for a product (e.g., Size, Color, Material). Option values are the specific choices within each type (e.g., Small, Medium, Large).
|
|
210
|
+
|
|
211
|
+
A product must have at least one option type to have multiple variants. Option types and their values are included in the product response when requested:
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
```typescript SDK
|
|
215
|
+
const product = await client.products.get('spree-tee', {
|
|
216
|
+
expand: ['option_types'],
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
product.option_types?.forEach(optionType => {
|
|
220
|
+
console.log(optionType.presentation) // "Size"
|
|
221
|
+
optionType.option_values.forEach(value => {
|
|
222
|
+
console.log(value.presentation) // "Small", "Medium", "Large"
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```bash cURL
|
|
228
|
+
curl 'https://api.mystore.com/api/v3/store/products/spree-tee?expand=option_types' \
|
|
229
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
> **INFO:** Option type `name` and `presentation` fields are translatable.
|
|
234
|
+
|
|
235
|
+
## Media
|
|
236
|
+
|
|
237
|
+
Media can be attached to the product (via the master variant) or to individual variants. When displaying a product, show the images for the selected variant, falling back to the product-level images.
|
|
238
|
+
|
|
239
|
+
### Thumbnails
|
|
240
|
+
|
|
241
|
+
Every product response includes a `thumbnail_url` field — the URL to the first image, ready to use without any expands. Similarly, each variant includes a `thumbnail_url` URL and an `media_count` counter.
|
|
242
|
+
|
|
243
|
+
Use these fields for product listing pages to avoid loading all images:
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
```typescript SDK
|
|
247
|
+
// List products — thumbnail_url is always included
|
|
248
|
+
const { data: products } = await client.products.list({ limit: 12 })
|
|
249
|
+
|
|
250
|
+
products.forEach(product => {
|
|
251
|
+
product.thumbnail_url // "https://cdn.../tote-front.jpg" — no expand needed
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```bash cURL
|
|
256
|
+
# thumbnail_url is always in the response — no ?expand needed
|
|
257
|
+
curl 'https://api.mystore.com/api/v3/store/products?limit=12' \
|
|
258
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
> **WARNING:** Avoid using `?expand=media` on listing pages. This loads **all** images for every product in the response, which is unnecessary when you only need a thumbnail. Use `thumbnail_url` instead and only expand full media on the product detail page.
|
|
263
|
+
|
|
264
|
+
### All Images
|
|
265
|
+
|
|
266
|
+
On the product detail page, expand `media` and `variants` to get the full set of images. Images are ordered by `position`:
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
```typescript SDK
|
|
270
|
+
const product = await client.products.get('spree-tote', {
|
|
271
|
+
expand: ['media', 'variants'],
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// Product-level images (from master variant)
|
|
275
|
+
product.media // [{ url: "https://cdn.../tote-front.jpg", position: 1 }, ...]
|
|
276
|
+
|
|
277
|
+
// Each variant has its own thumbnail and media_count
|
|
278
|
+
product.variants?.forEach(variant => {
|
|
279
|
+
variant.thumbnail // "https://cdn.../tote-red.jpg" — always available
|
|
280
|
+
variant.media_count // 3 — quick check without loading media
|
|
281
|
+
variant.media // full image array (only when ?expand=media)
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```bash cURL
|
|
286
|
+
curl 'https://api.mystore.com/api/v3/store/products/spree-tote?expand=media,variants' \
|
|
287
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
| Field | Available on | Always returned | Description |
|
|
292
|
+
|-------|-------------|:---:|-------------|
|
|
293
|
+
| `thumbnail_url` | Product | Yes | URL to the product's first media |
|
|
294
|
+
| `thumbnail_url` | Variant | Yes | URL to the variant's first media |
|
|
295
|
+
| `media_count` | Variant | Yes | Number of media |
|
|
296
|
+
| `media` | Product, Variant | No | Full image array (requires `?expand=media`) |
|
|
297
|
+
|
|
298
|
+
## Prices
|
|
299
|
+
|
|
300
|
+
Each variant can have multiple prices — one per currency, plus additional prices from [Price Lists](/developer/core-concepts/pricing) that apply conditionally based on market, geography, customer segment, or quantity.
|
|
301
|
+
|
|
302
|
+
The API automatically returns the correct price based on the current currency and market context:
|
|
303
|
+
|
|
304
|
+
| Field | Description |
|
|
305
|
+
|-------|-------------|
|
|
306
|
+
| `price` | Current selling price |
|
|
307
|
+
| `original_price` | Compare-at price (for showing strikethrough discounts) |
|
|
308
|
+
|
|
309
|
+
See the [Pricing](/developer/core-concepts/pricing) guide for details on Price Lists, Price Rules, and market-specific pricing.
|
|
310
|
+
|
|
311
|
+
## Categories
|
|
312
|
+
|
|
313
|
+
Categories provide a flexible way to organize products into hierarchical trees. Internally, Spree uses Taxonomies (category trees) and Taxons (nodes within those trees), but the Store API exposes them simply as **Categories**.
|
|
314
|
+
|
|
315
|
+
For example:
|
|
316
|
+
- **Categories** → Clothing → T-Shirts, Dresses
|
|
317
|
+
- **Brands** → Nike, Adidas, Puma
|
|
318
|
+
- **Collections** → Summer 2025, Best Sellers
|
|
319
|
+
|
|
320
|
+
Products can belong to multiple categories.
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
```typescript SDK
|
|
324
|
+
// List categories
|
|
325
|
+
const { data: categories } = await client.categories.list()
|
|
326
|
+
|
|
327
|
+
// Get a category by permalink
|
|
328
|
+
const category = await client.categories.get('clothing/shirts')
|
|
329
|
+
|
|
330
|
+
// List products in a category
|
|
331
|
+
const { data: products } = await client.categories.products.list('clothing/shirts', {
|
|
332
|
+
limit: 12,
|
|
333
|
+
})
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
```bash cURL
|
|
337
|
+
# List categories
|
|
338
|
+
curl 'https://api.mystore.com/api/v3/store/categories' \
|
|
339
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
340
|
+
|
|
341
|
+
# Get a category by permalink
|
|
342
|
+
curl 'https://api.mystore.com/api/v3/store/categories/clothing/shirts' \
|
|
343
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
344
|
+
|
|
345
|
+
# List products in a category
|
|
346
|
+
curl 'https://api.mystore.com/api/v3/store/categories/clothing/shirts/products?limit=12' \
|
|
347
|
+
-H 'Authorization: Bearer spree_pk_xxx'
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
> **INFO:** Category `name` and `description` fields are translatable.
|
|
352
|
+
|
|
353
|
+
## Related Documentation
|
|
354
|
+
|
|
355
|
+
- [Pricing](/developer/core-concepts/pricing) — Price Lists, Price Rules, and market-specific pricing
|
|
356
|
+
- [Inventory](/developer/core-concepts/inventory) — Stock management and backorders
|
|
357
|
+
- [Media](/developer/core-concepts/media) — Image management
|
|
358
|
+
- [Translations](/developer/core-concepts/translations) — Translating product content
|
|
359
|
+
- [Search & Filtering](/developer/core-concepts/search-filtering) — Full-text search and Ransack filtering
|
|
360
|
+
- [Querying](/api-reference/store-api/querying) — API filtering, sorting, and pagination
|