docs/developer/sdk/configuration.mdx
import LocalizationExample from '/snippets/store-api/localization-example.mdx'
Pass locale and currency headers with any request. For full details, see the Localization reference.
<LocalizationExample />// Works with all endpoints
const category = await client.categories.get('clothing/shirts', {
expand: ['ancestors'],
}, {
locale: 'de',
currency: 'EUR',
})
import { Since } from '/snippets/since.mdx';
Select which sales channel scopes a request. The SDK sends the value as the X-Spree-Channel header. Pass either the channel code (e.g. online, pos, wholesale) or the prefixed ID (ch_…) — code is preferred since it's merchant-meaningful and stable across environments. When omitted, the store's default channel is used.
// Client-level default — every request runs against the POS channel
const client = createClient({
baseUrl: 'https://api.mystore.com',
publishableKey: 'pk_xxx',
channel: 'pos',
})
// Sticky setter — switch channels mid-session (e.g. after the user
// changes storefront context)
client.setChannel('wholesale')
// Per-request override — one request only
const products = await client.products.list({}, { channel: 'pos' })
The channel scopes product visibility (only products published on that channel are returned), pricing fallback (per-channel pricing rules), and order attribution (a cart created with channel: 'pos' gets order.channel_id set to the POS channel).
import { SpreeError } from '@spree/sdk';
try {
await client.products.get('non-existent');
} catch (error) {
if (error instanceof SpreeError) {
console.log(error.code); // 'record_not_found'
console.log(error.message); // 'Product not found'
console.log(error.status); // 404
console.log(error.details); // Validation errors (if any)
}
}
You can provide a custom fetch implementation:
import { createClient } from '@spree/sdk';
const client = createClient({
baseUrl: 'https://api.mystore.com',
publishableKey: 'pk_xxx',
fetch: customFetchImplementation,
});
All monetary values in the API are returned as strings (e.g., "29.99", "0.0"), not numbers. This preserves decimal precision and avoids floating-point rounding issues.
const order = await client.orders.get('or_abc123', {}, { token });
// Monetary fields are strings
order.total; // "129.99"
order.item_total; // "99.99"
order.delivery_total; // "10.00"
order.tax_total; // "20.00"
// Display fields include currency formatting
order.display_total; // "$129.99"
// Convert to number when needed for calculations
const total = parseFloat(order.total);
This applies to all monetary fields across all types: Order, LineItem, Fulfillment, Payment, GiftCard, Price, etc.
The SDK includes full TypeScript support with generated types from the API serializers:
import type {
Product,
Order,
Cart,
Variant,
Category,
LineItem,
Address,
Customer,
PaginatedResponse,
} from '@spree/sdk';
// All responses are fully typed
const products: PaginatedResponse<Product> = await client.products.list();
const category: Category = await client.categories.get('clothing');
All types are exported as unprefixed names (e.g., Product, Order). Legacy Store* prefixed aliases (e.g., StoreProduct) are still available for backward compatibility.
Product - Product dataVariant - Variant dataCart - Cart data (uses cart_ prefixed IDs)Order - Completed order data (uses or_ prefixed IDs)LineItem - Line item in cartCategory - CategoryCountry - Country with statesState - State/provinceAddress - Customer addressCustomer - Customer profileMarket - Market configuration (currency, locales, countries)Payment - Payment recordPaymentMethod - Payment methodPaymentSession - Provider-agnostic payment sessionFulfillment - Fulfillment recordDeliveryRate - Delivery rate optionDeliveryMethod - Delivery methodCreditCard - Saved credit cardGiftCard - Gift cardDiscount - Discount applied to a cart or orderMedia - Product media (images, videos)Price - Price dataOptionType - Option type (e.g., Size, Color)OptionValue - Option value (e.g., Small, Red)DigitalLink - Digital download linkMetafield - Custom metafield dataWishlist - WishlistWishlistItem - Wishlist itemClient - Main client interfaceStoreClient - Store API client classClientConfig - Client configurationRequestOptions - Per-request optionsRetryConfig - Retry behavior configurationPaginatedResponse<T> - Paginated API responseListResponse<T> - List API responseAuthTokens - JWT tokens from loginAddressParams - Address input parametersUpdateCartParams - Cart update parametersCreatePaymentParams - Direct payment creation parametersCreatePaymentSessionParams - Payment session creation parametersUpdatePaymentSessionParams - Payment session update parametersCompletePaymentSessionParams - Payment session completion parametersProductFiltersResponse - Product filters responseCheckoutRequirement - Checkout requirement ({ step, field, message })All generated SDK types are TypeScript interfaces, which means you can extend them via declaration merging when you customize API serializers on the backend.
If you've customized the ProductSerializer in your app to include a brand_id attribute:
# app/serializers/my_store/product_serializer.rb
module MyStore
class ProductSerializer < Spree::Api::V3::ProductSerializer
typelize brand_id: :string
attribute :brand_id do |product|
product.brand&.prefixed_id
end
end
end
# config/initializers/spree.rb
Spree.api.product_serializer = MyStore::ProductSerializer
You can extend the SDK type in your frontend code so TypeScript knows about the new field:
// types/spree.d.ts
declare module '@spree/sdk' {
interface Product {
brand_id: string;
}
}
Now brand_id is available on every Product across your app — no type casting needed:
const products = await client.products.list();
products.data.forEach((product) => {
console.log(product.brand_id); // fully typed
});
If you use the SDK's Zod schemas for runtime validation, extend them with .extend():
import { z } from 'zod';
import { ProductSchema } from '@spree/sdk/zod';
const MyProductSchema = ProductSchema.extend({
brand_id: z.string(),
});
// Use for runtime validation
const product = MyProductSchema.parse(apiResponse);