# Create an allowed origin Source: https://spreecommerce.org/docs/api-reference/admin-api/allowed-origins/create-an-allowed-origin /api-reference/admin.yaml post /api/v3/admin/allowed_origins Adds an origin to the admin CORS allowlist. The value must be a bare `scheme://host[:port]` (no path, query, or fragment) and use `http` or `https`. **Required scope:** `write_settings` (for API-key authentication). # Delete an allowed origin Source: https://spreecommerce.org/docs/api-reference/admin-api/allowed-origins/delete-an-allowed-origin /api-reference/admin.yaml delete /api/v3/admin/allowed_origins/{id} Removes an origin from the admin CORS allowlist. After deletion the admin SPA running at that origin will no longer be able to call the admin API from a browser. **Required scope:** `write_settings` (for API-key authentication). # Get an allowed origin Source: https://spreecommerce.org/docs/api-reference/admin-api/allowed-origins/get-an-allowed-origin /api-reference/admin.yaml get /api/v3/admin/allowed_origins/{id} Returns a single allowed origin by prefixed ID. **Required scope:** `read_settings` (for API-key authentication). # List allowed origins Source: https://spreecommerce.org/docs/api-reference/admin-api/allowed-origins/list-allowed-origins /api-reference/admin.yaml get /api/v3/admin/allowed_origins Returns the CORS allowlist for the current store. Each entry is a bare `scheme://host[:port]` permitted to call the admin API from a browser. Backs the `Rack::Cors` allowlist and the CSRF boundary of the admin cookie session (see `docs/plans/5.5-admin-auth-cookie-refresh.md`). **Required scope:** `read_settings` (for API-key authentication). # Update an allowed origin Source: https://spreecommerce.org/docs/api-reference/admin-api/allowed-origins/update-an-allowed-origin /api-reference/admin.yaml patch /api/v3/admin/allowed_origins/{id} Updates an existing allowed origin. **Required scope:** `write_settings` (for API-key authentication). # Create an API key Source: https://spreecommerce.org/docs/api-reference/admin-api/api-keys/create-an-api-key /api-reference/admin.yaml post /api/v3/admin/api_keys Creates a publishable or secret API key. The plaintext token is included in the response **once** for secret keys; publishable keys expose their token on every read since they are intended for client-side use. **Required scope:** `write_settings` (for API-key authentication). # Delete an API key Source: https://spreecommerce.org/docs/api-reference/admin-api/api-keys/delete-an-api-key /api-reference/admin.yaml delete /api/v3/admin/api_keys/{id} **Required scope:** `write_settings` (for API-key authentication). # List API keys Source: https://spreecommerce.org/docs/api-reference/admin-api/api-keys/list-api-keys /api-reference/admin.yaml get /api/v3/admin/api_keys Returns publishable and secret API keys for the current store. Secret keys are listed by `token_prefix` only — the plaintext token is delivered exactly once on create. **Required scope:** `read_settings` (for API-key authentication). # Revoke an API key Source: https://spreecommerce.org/docs/api-reference/admin-api/api-keys/revoke-an-api-key /api-reference/admin.yaml patch /api/v3/admin/api_keys/{id}/revoke Marks the key revoked. Future requests using its token will fail; the row is preserved for audit. **Required scope:** `write_settings` (for API-key authentication). # Show an API key Source: https://spreecommerce.org/docs/api-reference/admin-api/api-keys/show-an-api-key /api-reference/admin.yaml get /api/v3/admin/api_keys/{id} **Required scope:** `read_settings` (for API-key authentication). # Authenticate Admin API requests with keys and JWTs Source: https://spreecommerce.org/docs/api-reference/admin-api/authentication Authenticate Spree Admin API requests using secret API keys for server-to-server calls or JWT bearer tokens for interactive admin sessions. The Admin API supports two authentication methods: **secret API keys** for server-to-server integrations, and **JWT bearer tokens** for admin SPA / interactive sessions. Every request must include credentials — there is no public surface. Secret API keys grant full administrative access to your store. **Never embed them in client-side code, mobile apps, or public repositories.** Use them only from secure server environments. ## Secret API key Pass the key via the `X-Spree-Api-Key` header: ```typescript SDK theme={"theme":"night-owl"} import { createAdminClient } from '@spree/admin-sdk' const client = createAdminClient({ baseUrl: 'https://store.example.com', secretKey: 'sk_xxx', }) // The SDK automatically sends the secret key with every request const { data: products } = await client.products.list() ``` ```bash cURL theme={"theme":"night-owl"} curl -X GET 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' ``` Secret API keys are prefixed with `sk_`. Create them in the Spree admin under **Settings → API Keys** or via the Spree CLI: ```bash theme={"theme":"night-owl"} spree api-key create --type secret # Create a new secret key spree api-key list # List existing keys spree api-key revoke # Revoke a key ``` If you omit the API key, the API returns `401 Unauthorized`: ```json theme={"theme":"night-owl"} { "error": { "code": "authentication_required", "message": "Authentication required" } } ``` ## JWT bearer token (admin user) For interactive admin sessions (the Spree admin SPA, custom dashboards, etc.) authenticate as an admin user and use the returned JWT token for subsequent requests. ### Login ```typescript SDK theme={"theme":"night-owl"} const { token, user } = await client.auth.login({ email: 'admin@example.com', password: 'secret123', }) // Reuse the JWT for subsequent requests. setToken is sticky — every // later call on `client` carries the bearer header automatically. client.setToken(token) const orders = await client.orders.list() ``` ```bash cURL theme={"theme":"night-owl"} curl -X POST 'https://store.example.com/api/v3/admin/auth/login' \ -H 'X-Spree-Api-Key: sk_xxx' \ -H 'Content-Type: application/json' \ -d '{"email": "admin@example.com", "password": "secret123"}' ``` ### Token refresh JWT tokens expire after 1 hour by default. Refresh them with the current token: ```typescript SDK theme={"theme":"night-owl"} const { token } = await client.auth.refresh({ token: currentToken }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X POST 'https://store.example.com/api/v3/admin/auth/refresh' \ -H 'X-Spree-Api-Key: sk_xxx' \ -H 'Authorization: Bearer ' ``` ## Permissions Authorization works differently depending on which credentials you use. ### Secret API keys: scopes Each secret API key carries a list of **scopes** that grant access to specific resources. Scopes follow a `read_` / `write_` convention; `write_` implies `read_`. | Scope | Endpoints | | -------------------------------------------- | ------------------------------------------------------------------------- | | `read_orders` / `write_orders` | `/orders/*`, `/orders/:id/items` | | `read_products` / `write_products` | `/products/*`, `/variants/*`, `/option_types/*`, `/media/*` | | `read_customers` / `write_customers` | `/customers/*`, `/customers/:id/addresses`, `/customers/:id/credit_cards` | | `read_payments` / `write_payments` | `/orders/:id/payments` | | `read_fulfillments` / `write_fulfillments` | `/orders/:id/fulfillments` | | `read_refunds` / `write_refunds` | `/orders/:id/refunds` | | `read_gift_cards` / `write_gift_cards` | `/orders/:id/gift_cards` | | `read_store_credits` / `write_store_credits` | `/customers/:id/store_credits`, `/orders/:id/store_credits` | | `read_categories` / `write_categories` | `/categories/*` | | `read_settings` / `write_settings` | `/payment_methods`, `/markets`, `/countries`, `/tax_categories`, `/store` | | `read_dashboard` | `/dashboard/*` (analytics) | Two convenience aliases: * **`read_all`** — every `read_*` scope * **`write_all`** — every `read_*` and `write_*` scope (full admin) If the key lacks the required scope, the API returns `403 Forbidden`: ```json theme={"theme":"night-owl"} { "error": { "code": "access_denied", "message": "API key lacks scope: write_orders", "details": { "required_scope": "write_orders" } } } ``` The `details.required_scope` field tells you exactly which scope to add. Pick scopes when creating the key in **Settings → API Keys**. Choose the narrowest set that covers your integration's needs. ### JWT bearer tokens: CanCanCan abilities JWT-authenticated admin users are authorized via [CanCanCan](https://github.com/CanCanCommunity/cancancan) abilities derived from their `Spree::Role`s. The SPA uses this fine-grained model to render UI conditionally; partial-permission staff users see only the resources their role grants. If the caller lacks permission for a specific action, the API returns `403 Forbidden`: ```json theme={"theme":"night-owl"} { "error": { "code": "access_denied", "message": "You are not authorized to perform this action" } } ``` ## Authentication summary | Method | Header | Use case | Authorization | | -------------- | ------------------------------- | ------------------------------- | ------------------- | | Secret API key | `X-Spree-Api-Key: sk_xxx` | Server-to-server integrations | Scopes | | JWT token | `Authorization: Bearer ` | Interactive admin sessions; SPA | CanCanCan abilities | If both headers are present, the JWT token wins: CanCanCan applies and scopes are ignored. This lets you use `sk_xxx` to bootstrap a session and then issue per-user JWTs for individual admin actions. # Get current admin user and permissions Source: https://spreecommerce.org/docs/api-reference/admin-api/authentication/get-current-admin-user-and-permissions /api-reference/admin.yaml get /api/v3/admin/me Returns the current admin user profile and a serialized list of permissions (CanCanCan rules). The SPA uses these to drive UI permission checks. # Login Source: https://spreecommerce.org/docs/api-reference/admin-api/authentication/login /api-reference/admin.yaml post /api/v3/admin/auth/login Authenticates an admin user and returns a short-lived JWT access token. The rotatable refresh token is set in an HttpOnly cookie — it is not included in the response body. Dispatches by the `provider` field to a strategy registered in `Spree.admin_authentication_strategies`. When `provider` is omitted it defaults to `email`, which uses the built-in email/password strategy. To plug in a third-party identity provider (Okta, Azure AD, Google Workspace SSO, a custom JWT issuer, SAML, etc.), register a `Spree::Authentication::Strategies::BaseStrategy` subclass under a provider key, then send `{ "provider": "", ... }` with the fields your strategy requires. The endpoint returns the same Spree-issued JWT regardless of which strategy authenticated the request. # Logout Source: https://spreecommerce.org/docs/api-reference/admin-api/authentication/logout /api-reference/admin.yaml post /api/v3/admin/auth/logout Revokes the refresh-token cookie, effectively logging the admin out. # Refresh token Source: https://spreecommerce.org/docs/api-reference/admin-api/authentication/refresh-token /api-reference/admin.yaml post /api/v3/admin/auth/refresh Exchanges the HttpOnly refresh-token cookie for a new access JWT and a rotated refresh token cookie. No request body or Authorization header is required — the cookie alone authenticates the call. # Create a channel Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/create-a-channel /api-reference/admin.yaml post /api/v3/admin/channels Creates a new channel on the current store. `code` is normalized to a URL-safe slug (`Point of Sale` → `point-of-sale`); when omitted it's derived from `name`. **Required scope:** `write_settings` (for API-key authentication). # Delete a channel Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/delete-a-channel /api-reference/admin.yaml delete /api/v3/admin/channels/{id} **Required scope:** `write_settings` (for API-key authentication). # Get a channel Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/get-a-channel /api-reference/admin.yaml get /api/v3/admin/channels/{id} **Required scope:** `read_settings` (for API-key authentication). # List channels Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/list-channels /api-reference/admin.yaml get /api/v3/admin/channels Returns the channels configured for the current store. **Required scope:** `read_settings` (for API-key authentication). # Publish products on a channel Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/publish-products-on-a-channel /api-reference/admin.yaml post /api/v3/admin/channels/{id}/add_products Publishes the listed products on this channel. Idempotent — re-publishing an already-published product updates its publication window. Products from sibling stores are silently dropped. **Required scope:** `write_products` (for API-key authentication). # Unpublish products from a channel Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/unpublish-products-from-a-channel /api-reference/admin.yaml post /api/v3/admin/channels/{id}/remove_products Unpublishes the listed products from this channel. **Required scope:** `write_products` (for API-key authentication). # Update a channel Source: https://spreecommerce.org/docs/api-reference/admin-api/channels/update-a-channel /api-reference/admin.yaml patch /api/v3/admin/channels/{id} **Required scope:** `write_settings` (for API-key authentication). # Create a custom field definition Source: https://spreecommerce.org/docs/api-reference/admin-api/custom-fields/create-a-custom-field-definition /api-reference/admin.yaml post /api/v3/admin/custom_field_definitions **Required scope:** `write_custom_field_definitions` (for API-key authentication). # Delete a custom field definition Source: https://spreecommerce.org/docs/api-reference/admin-api/custom-fields/delete-a-custom-field-definition /api-reference/admin.yaml delete /api/v3/admin/custom_field_definitions/{id} Deletes the definition and cascades to all custom field values referencing it. **Required scope:** `write_custom_field_definitions` (for API-key authentication). # List custom field definitions Source: https://spreecommerce.org/docs/api-reference/admin-api/custom-fields/list-custom-field-definitions /api-reference/admin.yaml get /api/v3/admin/custom_field_definitions Returns all defined custom fields. Filter by `?q[resource_type_eq]=Spree::Product` to narrow to one parent type. **Required scope:** `read_custom_field_definitions` (for API-key authentication). # Show a custom field definition Source: https://spreecommerce.org/docs/api-reference/admin-api/custom-fields/show-a-custom-field-definition /api-reference/admin.yaml get /api/v3/admin/custom_field_definitions/{id} **Required scope:** `read_custom_field_definitions` (for API-key authentication). # Update a custom field definition Source: https://spreecommerce.org/docs/api-reference/admin-api/custom-fields/update-a-custom-field-definition /api-reference/admin.yaml patch /api/v3/admin/custom_field_definitions/{id} **Required scope:** `write_custom_field_definitions` (for API-key authentication). # Create a customer group Source: https://spreecommerce.org/docs/api-reference/admin-api/customer-groups/create-a-customer-group /api-reference/admin.yaml post /api/v3/admin/customer_groups Creates a customer group in the current store. `customer_ids` is optional; when present, customers are attached at create time. Pass prefixed IDs (e.g. `cus_…`) — the server decodes them automatically. **Required scope:** `write_customers` (for API-key authentication). # Delete a customer group Source: https://spreecommerce.org/docs/api-reference/admin-api/customer-groups/delete-a-customer-group /api-reference/admin.yaml delete /api/v3/admin/customer_groups/{id} Soft-deletes the group. Member users are not deleted; their `customer_group_users` rows are dropped. **Required scope:** `write_customers` (for API-key authentication). # Get a customer group Source: https://spreecommerce.org/docs/api-reference/admin-api/customer-groups/get-a-customer-group /api-reference/admin.yaml get /api/v3/admin/customer_groups/{id} Returns a single customer group. Pass `?expand=customers` to embed the full member list inline (recommended only for single-record reads — embed cost scales with membership size). **Required scope:** `read_customers` (for API-key authentication). # List customer groups Source: https://spreecommerce.org/docs/api-reference/admin-api/customer-groups/list-customer-groups /api-reference/admin.yaml get /api/v3/admin/customer_groups Returns the customer groups configured for the current store. Groups segment customers for targeted promotions (see the `customer_group` promotion rule) and reporting. The list endpoint never embeds the member list — fetch a single group with `?expand=customers` if you need them inline, or query `/admin/customers?customer_group_id_in=…` for paginated membership. **Required scope:** `read_customers` (for API-key authentication). # Update a customer group Source: https://spreecommerce.org/docs/api-reference/admin-api/customer-groups/update-a-customer-group /api-reference/admin.yaml patch /api/v3/admin/customer_groups/{id} Updates name, description, or membership. `customer_ids` is a full-set replacement — the server reconciles the membership to match the array, adding new IDs and removing ones not present. Send `customer_ids: []` to clear all members. **Required scope:** `write_customers` (for API-key authentication). # Bulk-add customers to groups Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/bulk-add-customers-to-groups /api-reference/admin.yaml post /api/v3/admin/customers/bulk_add_to_groups Attaches each customer in `ids` to every group in `customer_group_ids`. Idempotent — customers already in a group are skipped server-side. Groups from sibling stores are silently ignored. Returns counts of customers and groups that were processed (post store-scoping). **Required scope:** `write_customers` (for API-key authentication). # Bulk-add tags to customers Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/bulk-add-tags-to-customers /api-reference/admin.yaml post /api/v3/admin/customers/bulk_add_tags Adds each tag name in `tags` to every customer in `ids`. Tags are upserted by name; re-adding an existing tag is a no-op. **Required scope:** `write_customers` (for API-key authentication). # Bulk-remove customers from groups Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/bulk-remove-customers-from-groups /api-reference/admin.yaml post /api/v3/admin/customers/bulk_remove_from_groups Detaches each customer in `ids` from every group in `customer_group_ids`. No-op for non-members. Groups from sibling stores are silently ignored. **Required scope:** `write_customers` (for API-key authentication). # Bulk-remove tags from customers Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/bulk-remove-tags-from-customers /api-reference/admin.yaml post /api/v3/admin/customers/bulk_remove_tags Removes each tag name in `tags` from every customer in `ids`. No-op for customers that don't carry the tag. **Required scope:** `write_customers` (for API-key authentication). # Create a customer Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/create-a-customer /api-reference/admin.yaml post /api/v3/admin/customers Creates a customer. No welcome email is sent automatically. **Required scope:** `write_customers` (for API-key authentication). # Create a customer address Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/create-a-customer-address /api-reference/admin.yaml post /api/v3/admin/customers/{customer_id}/addresses Adds a new address to the customer's address book. Pass `is_default_billing: true` or `is_default_shipping: true` to set as the default — the previous default loses its flag in the same transaction. **Required scope:** `write_customers` (for API-key authentication). # Delete a customer Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/delete-a-customer /api-reference/admin.yaml delete /api/v3/admin/customers/{id} Deletes a customer. Returns 422 if the customer has any orders. **Required scope:** `write_customers` (for API-key authentication). # Delete a customer address Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/delete-a-customer-address /api-reference/admin.yaml delete /api/v3/admin/customers/{customer_id}/addresses/{id} Deletes the address. If it was a default, the customer loses that default (no auto-promotion). **Required scope:** `write_customers` (for API-key authentication). # Delete a customer credit card Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/delete-a-customer-credit-card /api-reference/admin.yaml delete /api/v3/admin/customers/{customer_id}/credit_cards/{id} Deletes a saved credit card. **Required scope:** `write_customers` (for API-key authentication). # Delete a store credit Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/delete-a-store-credit /api-reference/admin.yaml delete /api/v3/admin/customers/{customer_id}/store_credits/{id} Deletes an unused store credit (amount_used == 0). Returns 422 otherwise. **Required scope:** `write_store_credits` (for API-key authentication). # Issue a store credit to a customer Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/issue-a-store-credit-to-a-customer /api-reference/admin.yaml post /api/v3/admin/customers/{customer_id}/store_credits `created_by` is set automatically from the authenticated admin. **Required scope:** `write_store_credits` (for API-key authentication). # List customer addresses Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/list-customer-addresses /api-reference/admin.yaml get /api/v3/admin/customers/{customer_id}/addresses Returns the customer's saved addresses. **Required scope:** `read_customers` (for API-key authentication). # List customer credit cards Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/list-customer-credit-cards /api-reference/admin.yaml get /api/v3/admin/customers/{customer_id}/credit_cards Returns the customer's saved credit cards. Useful for off-session admin charges via `POST /admin/orders/:id/payments { source_id }`. **Required scope:** `read_customers` (for API-key authentication). # List customer store credits Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/list-customer-store-credits /api-reference/admin.yaml get /api/v3/admin/customers/{customer_id}/store_credits Returns store credits issued to the customer. **Required scope:** `read_store_credits` (for API-key authentication). # List customers Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/list-customers /api-reference/admin.yaml get /api/v3/admin/customers Returns a paginated list of customers. Supports Ransack search/filters. **Required scope:** `read_customers` (for API-key authentication). # Show a customer Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/show-a-customer /api-reference/admin.yaml get /api/v3/admin/customers/{id} Returns full customer details including computed order stats (orders_count, total_spent, last_order_completed_at). **Required scope:** `read_customers` (for API-key authentication). # Show a customer credit card Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/show-a-customer-credit-card /api-reference/admin.yaml get /api/v3/admin/customers/{customer_id}/credit_cards/{id} Returns a saved credit card by ID. **Required scope:** `read_customers` (for API-key authentication). # Update a customer Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/update-a-customer /api-reference/admin.yaml patch /api/v3/admin/customers/{id} Updates customer attributes. `tags` replaces the full set. **Required scope:** `write_customers` (for API-key authentication). # Update a customer address Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/update-a-customer-address /api-reference/admin.yaml patch /api/v3/admin/customers/{customer_id}/addresses/{id} Updates a customer address. **Required scope:** `write_customers` (for API-key authentication). # Update a store credit Source: https://spreecommerce.org/docs/api-reference/admin-api/customers/update-a-store-credit /api-reference/admin.yaml patch /api/v3/admin/customers/{customer_id}/store_credits/{id} Update memo / category / amount. The amount can only be changed if `amount_used == 0`. **Required scope:** `write_store_credits` (for API-key authentication). # Admin API error responses, status codes, and handling Source: https://spreecommerce.org/docs/api-reference/admin-api/errors Reference for the Spree Admin API Stripe-style error response format, HTTP status codes, and admin-specific error codes returned by endpoints. The Admin API uses the same Stripe-style error format as the rest of the Spree v3 API. Every error response carries a machine-readable `code` and a human-readable `message`. ## Error response format ```json theme={"theme":"night-owl"} { "error": { "code": "record_not_found", "message": "Order not found" } } ``` Validation errors include a `details` field with per-field error messages: ```json theme={"theme":"night-owl"} { "error": { "code": "validation_error", "message": "Email is invalid and Phone can't be blank", "details": { "email": ["is invalid"], "phone": ["can't be blank"] } } } ``` Some specialized errors carry structured `details` (for example, scope errors include the `required_scope`): ```json theme={"theme":"night-owl"} { "error": { "code": "access_denied", "message": "API key lacks scope: write_orders", "details": { "required_scope": "write_orders" } } } ``` ### Schema | Field | Type | Description | | --------------- | -------- | ------------------------------------------------------------------------------------ | | `error.code` | `string` | Machine-readable error code (see tables below) | | `error.message` | `string` | Human-readable description of the error | | `error.details` | `object` | Optional. Field-specific validation errors or structured context (varies by `code`). | ## HTTP status codes | Status | Meaning | When | | ------ | --------------------- | -------------------------------------------------------------------------------------------- | | `400` | Bad Request | Missing required parameters, malformed JSON | | `401` | Unauthorized | Missing or invalid API key, expired or invalid JWT token | | `403` | Forbidden | Authenticated but lacks permission (CanCanCan ability) or scope | | `404` | Not Found | Resource doesn't exist, isn't accessible to the calling key, or belongs to a different store | | `409` | Conflict | Resource was modified by a concurrent request (optimistic locking) | | `422` | Unprocessable Content | Validation failed, invalid state transition, business rule violation | | `429` | Too Many Requests | Login/refresh rate limit exceeded | ## Authentication & authorization | Code | Status | Description | | -------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `authentication_required` | 401 | Request reached a protected endpoint with no credentials | | `authentication_failed` | 401 | Login email/password was wrong | | `invalid_token` | 401 | API key or JWT token is invalid or expired | | `invalid_refresh_token` | 401 | Refresh token is invalid or expired | | `access_denied` | 403 | Caller lacks permission. For API-key callers, `details.required_scope` indicates the missing scope (see [Authentication](/docs/api-reference/admin-api/authentication#permissions)). For JWT callers, the user's role lacks the CanCanCan ability for this action. | | `current_password_invalid` | 422 | Current password is wrong (when changing password) | ## Resources | Code | Status | Description | | ------------------ | ------ | --------------------------------------------------------------------- | | `record_not_found` | 404 | Resource doesn't exist or isn't accessible in the calling key's store | | `resource_invalid` | 422 | Resource couldn't be saved | ## Validation | Code | Status | Description | | ------------------- | ------ | ----------------------------------------------------------------------- | | `validation_error` | 422 | Model validation failed. Inspect `details` for field-specific messages. | | `parameter_missing` | 400 | A required parameter is missing | | `parameter_invalid` | 400 | A parameter has an invalid value | ## Orders | Code | Status | Description | | ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------- | | `cart_cannot_transition` | 422 | Order state machine refused the transition (e.g., completing an order without an address or payment) | | `cart_already_updated` | 409 | Order was modified by a concurrent request. Refetch and retry. See [optimistic locking](#optimistic-locking) below. | | `cart_invalid_state` | 422 | Order is in a state that doesn't allow this operation | | `cart_empty` | 422 | Cannot complete an order with no line items | ## Customers | Code | Status | Description | | --------------------- | ------ | ------------------------------------------------------------------ | | `customer_has_orders` | 422 | Cannot delete a customer with completed orders. Anonymize instead. | ## Store credits | Code | Status | Description | | --------------------- | ------ | --------------------------------------------------------------------------------------- | | `store_credit_in_use` | 422 | Cannot edit `amount` on or delete a store credit that has been partially or fully used. | ## Tags | Code | Status | Description | | ----------------------- | ------ | ------------------------------------------------------------------------------------------------------------------ | | `invalid_taggable_type` | 422 | The `taggable_type` query param isn't one of the allowed values (`Spree::Product`, `Spree::Order`, `Spree::User`). | ## Payments | Code | Status | Description | | -------------------------- | ------ | ----------------------------------------------------------- | | `payment_failed` | 422 | Payment was declined by the gateway | | `payment_processing_error` | 422 | Spree couldn't process the payment due to an internal error | | `gateway_error` | 422 | Payment gateway returned an error | ## Examples ### Insufficient scope (API key) ```bash theme={"theme":"night-owl"} curl -X POST 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ -H 'Content-Type: application/json' \ -d '{"email": "test@example.com"}' ``` ```json 403 theme={"theme":"night-owl"} { "error": { "code": "access_denied", "message": "API key lacks scope: write_orders", "details": { "required_scope": "write_orders" } } } ``` ### Validation error (customer create) ```bash theme={"theme":"night-owl"} curl -X POST 'https://store.example.com/api/v3/admin/customers' \ -H 'X-Spree-Api-Key: sk_xxx' \ -H 'Content-Type: application/json' \ -d '{}' ``` ```json 422 theme={"theme":"night-owl"} { "error": { "code": "validation_error", "message": "Email can't be blank", "details": { "email": ["can't be blank"] } } } ``` ### Customer with completed orders ```bash theme={"theme":"night-owl"} curl -X DELETE 'https://store.example.com/api/v3/admin/customers/cus_xxx' \ -H 'X-Spree-Api-Key: sk_xxx' ``` ```json 422 theme={"theme":"night-owl"} { "error": { "code": "customer_has_orders", "message": "Cannot delete customer with completed orders" } } ``` ### Concurrent order update ```bash theme={"theme":"night-owl"} curl -X PATCH 'https://store.example.com/api/v3/admin/orders/or_xxx' \ -H 'X-Spree-Api-Key: sk_xxx' \ -H 'Content-Type: application/json' \ -d '{"email": "new@example.com"}' ``` ```json 409 theme={"theme":"night-owl"} { "error": { "code": "cart_already_updated", "message": "Order was modified by another request. Refetch and retry." } } ``` ## Handling errors with the SDK `@spree/admin-sdk` throws a `SpreeError` for every non-2xx response: ```typescript theme={"theme":"night-owl"} import { SpreeError } from '@spree/admin-sdk' try { await client.orders.update(orderId, { email: 'new@example.com' }) } catch (error) { if (error instanceof SpreeError) { console.log(error.code) // 'cart_already_updated' console.log(error.message) // 'Order was modified by another request...' console.log(error.status) // 409 console.log(error.details) // undefined or { ... } } } ``` ### Common patterns **Branch on error code**: ```typescript theme={"theme":"night-owl"} try { await client.customers.delete(customerId) } catch (error) { if (error instanceof SpreeError && error.code === 'customer_has_orders') { // Anonymize instead of deleting return anonymizeCustomer(customerId) } throw error } ``` **Retry on optimistic-lock conflicts**: ```typescript theme={"theme":"night-owl"} async function updateOrderWithRetry(id, params, attempts = 3) { for (let i = 0; i < attempts; i++) { try { return await client.orders.update(id, params) } catch (error) { if (error instanceof SpreeError && error.code === 'cart_already_updated' && i < attempts - 1) { continue } throw error } } } ``` **Show field-level validation errors**: ```typescript theme={"theme":"night-owl"} try { await client.customers.create(customerData) } catch (error) { if (error instanceof SpreeError && error.details) { for (const [field, messages] of Object.entries(error.details)) { setFieldError(field, (messages as string[]).join(', ')) } } } ``` ## Optimistic locking Orders use a `state_lock_version` column to detect concurrent modifications. Every state-changing operation increments it; if two callers update the same order simultaneously, the second write fails with `cart_already_updated` (409) — refetch and retry. This protects against race conditions when multiple clients (or the same client, retried) try to mutate the same order. Combined with idempotency at the integration level (e.g., dedupe webhook deliveries by event ID), it makes admin order management safe under concurrency. # Create an export Source: https://spreecommerce.org/docs/api-reference/admin-api/exports/create-an-export /api-reference/admin.yaml post /api/v3/admin/exports Queues a CSV export. The `type` selects the dataset; `search_params` is an optional Ransack query (same shape used by the `q[...]` params on list endpoints) that filters which records are exported. Pass `record_selection: "all"` to clear the filter server-side and export everything in scope. Generation is asynchronous. Poll `GET /admin/exports/{id}` until `done` is `true`, then redirect the browser to `download_url` to fetch the file. **Required scope:** `write_exports` (for API-key authentication). # Delete an export Source: https://spreecommerce.org/docs/api-reference/admin-api/exports/delete-an-export /api-reference/admin.yaml delete /api/v3/admin/exports/{id} Removes the export and purges its attachment. **Required scope:** `write_exports` (for API-key authentication). # Download an export Source: https://spreecommerce.org/docs/api-reference/admin-api/exports/download-an-export /api-reference/admin.yaml get /api/v3/admin/exports/{id}/download Streams the exported CSV with `Content-Disposition: attachment`. Returns 422 with `code: export_not_ready` while `done` is still `false`. The endpoint is JWT/API-key protected, so SPA clients must fetch it (with `Authorization` header) and trigger the browser download via a Blob URL — a top-level navigation cannot carry the JWT. **Required scope:** `read_exports` (for API-key authentication). # List exports Source: https://spreecommerce.org/docs/api-reference/admin-api/exports/list-exports /api-reference/admin.yaml get /api/v3/admin/exports Returns CSV exports queued or completed for the current store. Polled by the admin SPA to detect when an export finishes (`done: true`). **Required scope:** `read_exports` (for API-key authentication). # Show an export Source: https://spreecommerce.org/docs/api-reference/admin-api/exports/show-an-export /api-reference/admin.yaml get /api/v3/admin/exports/{id} Returns export status. While `done` is `false`, the SPA continues polling. **Required scope:** `read_exports` (for API-key authentication). # Cancel a fulfillment Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/cancel-a-fulfillment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/fulfillments/{id}/cancel Cancels a fulfillment. **Required scope:** `write_fulfillments` (for API-key authentication). # Fulfill a fulfillment Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/fulfill-a-fulfillment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/fulfillments/{id}/fulfill Marks a fulfillment as fulfilled. **Required scope:** `write_fulfillments` (for API-key authentication). # List fulfillments Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/list-fulfillments /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/fulfillments Returns all shipments for an order. **Required scope:** `read_fulfillments` (for API-key authentication). # Resume a fulfillment Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/resume-a-fulfillment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/fulfillments/{id}/resume Resumes a canceled fulfillment. **Required scope:** `write_fulfillments` (for API-key authentication). # Show a shipment Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/show-a-shipment /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/fulfillments/{id} Returns details of a specific shipment. **Required scope:** `read_fulfillments` (for API-key authentication). # Split a fulfillment Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/split-a-fulfillment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/fulfillments/{id}/split Transfers items from this shipment to a new shipment at a different stock location. **Required scope:** `write_fulfillments` (for API-key authentication). # Update a shipment Source: https://spreecommerce.org/docs/api-reference/admin-api/fulfillments/update-a-shipment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/fulfillments/{id} Updates a shipment (tracking, shipping rate). **Required scope:** `write_fulfillments` (for API-key authentication). # Create a gift card Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/create-a-gift-card /api-reference/admin.yaml post /api/v3/admin/gift_cards Issues a gift card scoped to the current store. The code is auto-generated when omitted. `currency` defaults to the store's configured currency. Pass `user_id` (prefixed ID) to attach the card to a specific customer. **Required scope:** `write_gift_cards` (for API-key authentication). # Create a gift card batch Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/create-a-gift-card-batch /api-reference/admin.yaml post /api/v3/admin/gift_card_batches Issues a batch of gift cards in a single call. The server generates `codes_count` cards inline for small batches (configurable via `Spree.config.gift_card_batch_web_limit`, default 500) or enqueues a background job for larger ones. Each card's code is the batch `prefix` followed by random hex. **Required scope:** `write_gift_cards` (for API-key authentication). # Delete a gift card Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/delete-a-gift-card /api-reference/admin.yaml delete /api/v3/admin/gift_cards/{id} Deletes an unused gift card. Cards that have been redeemed or partially redeemed cannot be deleted and return 422. **Required scope:** `write_gift_cards` (for API-key authentication). # Get a gift card Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/get-a-gift-card /api-reference/admin.yaml get /api/v3/admin/gift_cards/{id} Returns a single gift card. **Required scope:** `read_gift_cards` (for API-key authentication). # Get a gift card batch Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/get-a-gift-card-batch /api-reference/admin.yaml get /api/v3/admin/gift_card_batches/{id} Returns a single batch. **Required scope:** `read_gift_cards` (for API-key authentication). # List gift card batches Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/list-gift-card-batches /api-reference/admin.yaml get /api/v3/admin/gift_card_batches Returns the gift card batches issued by the current store. Each batch groups the cards generated together for a campaign or bulk-issuance — the cards themselves live under `/admin/gift_cards` and reference the batch via `gift_card_batch_id`. **Required scope:** `read_gift_cards` (for API-key authentication). # List gift cards Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/list-gift-cards /api-reference/admin.yaml get /api/v3/admin/gift_cards Returns the gift cards issued by the current store. Filter by `q[code_cont]` for code search, `q[user_id_eq]` for cards issued to a specific customer, or `q[state_eq]` for status filtering. **Required scope:** `read_gift_cards` (for API-key authentication). # Update a gift card Source: https://spreecommerce.org/docs/api-reference/admin-api/gift-cards/update-a-gift-card /api-reference/admin.yaml patch /api/v3/admin/gift_cards/{id} Updates an active gift card's editable attributes. Redeemed or partially-redeemed cards cannot be edited. **Required scope:** `write_gift_cards` (for API-key authentication). # Spree Admin API introduction and SDK quick start Source: https://spreecommerce.org/docs/api-reference/admin-api/introduction Overview of the Spree Admin REST API for managing products, orders, customers, and fulfillments, with SDK install and quick start examples. The Admin API is a REST API for managing Spree stores programmatically — products, orders, customers, fulfillments, payments, and more. It is intended for backend integrations, custom admin tooling, and automation. All routes are prefixed with `/api/v3/admin`. During development the API is available under `http://localhost:3000/api/v3/admin`. For production, replace `http://localhost:3000` with your Spree application URL. ## Admin API vs Store API | | Admin API | Store API | | -------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------- | | **Purpose** | Manage store data | Power storefronts | | **Audience** | Staff users, backend integrations | Customers, storefronts | | **Authentication** | Secret API key (`sk_…`) or admin JWT | Publishable API key (`pk_…`), customer JWT, order token | | **Permissions** | API key scopes (API key authentication) or Admin Staff permission sets | Customer can only read/modify their own data | | **Write operations** | Full CRUD on most resources | Limited to the current customer's cart, addresses, profile | If you're building a storefront, use the [Store API](/docs/api-reference/store-api/introduction). The Admin API exposes administrative operations that should never be invoked from a browser. ## Using the SDK We recommend using `@spree/admin-sdk` to interact with the Admin API. It provides typed clients, automatic retries, and idempotency support. ### Installation ```bash theme={"theme":"night-owl"} npm install @spree/admin-sdk # or yarn add @spree/admin-sdk # or pnpm add @spree/admin-sdk ``` ### Quick start ```typescript theme={"theme":"night-owl"} import { createAdminClient } from '@spree/admin-sdk' const client = createAdminClient({ baseUrl: 'http://localhost:3000', secretKey: 'sk_xxx', }) const { data: orders } = await client.orders.list({ status_eq: 'complete', limit: 25, }) ``` Before integrating, read: * [Authentication](/docs/api-reference/admin-api/authentication) — secret API keys, scopes, and JWT tokens * [Errors](/docs/api-reference/admin-api/errors) — error format and admin-specific codes * [Querying](/docs/api-reference/admin-api/querying) — filtering, sorting, pagination, and `expand` # Create a market Source: https://spreecommerce.org/docs/api-reference/admin-api/markets/create-a-market /api-reference/admin.yaml post /api/v3/admin/markets Creates a new market for the current store. - `country_isos` accepts 2-letter ISO country codes (e.g. `["DE", "FR"]`); the market must contain at least one country. Unknown codes are silently dropped. - `supported_locales` accepts an array of locale codes; the `default_locale` is always implicitly included. - Setting `default: true` automatically demotes the previous default market in the store. **Required scope:** `write_settings` (for API-key authentication). # Delete a market Source: https://spreecommerce.org/docs/api-reference/admin-api/markets/delete-a-market /api-reference/admin.yaml delete /api/v3/admin/markets/{id} Soft-deletes the market (sets `deleted_at`). The default market and the last remaining market in a store cannot be deleted — both return 422 with a `validation_error`. **Required scope:** `write_settings` (for API-key authentication). # Get a market Source: https://spreecommerce.org/docs/api-reference/admin-api/markets/get-a-market /api-reference/admin.yaml get /api/v3/admin/markets/{id} **Required scope:** `read_settings` (for API-key authentication). # List markets Source: https://spreecommerce.org/docs/api-reference/admin-api/markets/list-markets /api-reference/admin.yaml get /api/v3/admin/markets Returns the markets configured for the current store. Markets are store-scoped and ordered by `position` (an `acts_as_list` column — update the order via `PATCH /markets/{id}` with `position`). **Required scope:** `read_settings` (for API-key authentication). # Update a market Source: https://spreecommerce.org/docs/api-reference/admin-api/markets/update-a-market /api-reference/admin.yaml patch /api/v3/admin/markets/{id} Updates an existing market. Pass `country_isos` to replace the market's country list (full-set update), `supported_locales` to replace the supported locales, or `position` to reorder the market within the store. Setting `default: true` automatically demotes the previous default. **Required scope:** `write_settings` (for API-key authentication). # Create an option type Source: https://spreecommerce.org/docs/api-reference/admin-api/option-types/create-an-option-type /api-reference/admin.yaml post /api/v3/admin/option_types Creates a new option type. Supports nested option values. Option values can be provided inline and will be created or updated by name. **Required scope:** `write_products` (for API-key authentication). # Delete an option type Source: https://spreecommerce.org/docs/api-reference/admin-api/option-types/delete-an-option-type /api-reference/admin.yaml delete /api/v3/admin/option_types/{id} Deletes an option type. **Required scope:** `write_products` (for API-key authentication). # Get an option type Source: https://spreecommerce.org/docs/api-reference/admin-api/option-types/get-an-option-type /api-reference/admin.yaml get /api/v3/admin/option_types/{id} Returns a single option type by ID, including its option values. **Required scope:** `read_products` (for API-key authentication). # List option types Source: https://spreecommerce.org/docs/api-reference/admin-api/option-types/list-option-types /api-reference/admin.yaml get /api/v3/admin/option_types Returns a paginated list of option types. **Required scope:** `read_products` (for API-key authentication). # Update an option type Source: https://spreecommerce.org/docs/api-reference/admin-api/option-types/update-an-option-type /api-reference/admin.yaml patch /api/v3/admin/option_types/{id} Updates an option type. Supports updating nested option values. **Required scope:** `write_products` (for API-key authentication). # Add an item Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/add-an-item /api-reference/admin.yaml post /api/v3/admin/orders/{order_id}/items Adds a new line item to the order. **Required scope:** `write_orders` (for API-key authentication). # Apply a gift card to an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/apply-a-gift-card-to-an-order /api-reference/admin.yaml post /api/v3/admin/orders/{order_id}/gift_cards Applies a gift card by code to the order. Returns the gift card. **Required scope:** `write_gift_cards` (for API-key authentication). # Apply customer's store credit to an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/apply-customers-store-credit-to-an-order /api-reference/admin.yaml post /api/v3/admin/orders/{order_id}/store_credits Applies the order customer's available store credit. When `amount` is omitted, applies up to the order outstanding balance. **Required scope:** `write_store_credits` (for API-key authentication). # Approve an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/approve-an-order /api-reference/admin.yaml patch /api/v3/admin/orders/{id}/approve Approves an order (e.g., for fraud review). **Required scope:** `write_orders` (for API-key authentication). # Cancel an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/cancel-an-order /api-reference/admin.yaml patch /api/v3/admin/orders/{id}/cancel Cancels a completed order. **Required scope:** `write_orders` (for API-key authentication). # Complete an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/complete-an-order /api-reference/admin.yaml patch /api/v3/admin/orders/{id}/complete Completes an order in the `confirm` state, marking it as placed. Set `notify_customer: true` to send the order confirmation email. **Required scope:** `write_orders` (for API-key authentication). # Create a draft order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/create-a-draft-order /api-reference/admin.yaml post /api/v3/admin/orders Creates a new draft order in one shot. Customer, items, addresses, currency, market, channel, locale, notes, metadata, and a coupon code can all be provided inline. Setting `preferred_stock_location_id` pins the order's preferred fulfillment location — Order Routing's built-in `PreferredLocation` rule consumes it when picking which stock location ships each shipment. Invalid coupon codes are non-fatal — the order is created and the failure is reported on the service result (not in the API response body for now). **Required scope:** `write_orders` (for API-key authentication). # Delete a draft order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/delete-a-draft-order /api-reference/admin.yaml delete /api/v3/admin/orders/{id} Deletes a draft order. Completed orders cannot be deleted. **Required scope:** `write_orders` (for API-key authentication). # List order items Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/list-order-items /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/items Returns all line items for an order. **Required scope:** `read_orders` (for API-key authentication). # List orders Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/list-orders /api-reference/admin.yaml get /api/v3/admin/orders Returns a paginated list of orders for the current store. **Required scope:** `read_orders` (for API-key authentication). # Remove a gift card from an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/remove-a-gift-card-from-an-order /api-reference/admin.yaml delete /api/v3/admin/orders/{order_id}/gift_cards/{id} Removes the gift card from the order. **Required scope:** `write_gift_cards` (for API-key authentication). # Remove an item Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/remove-an-item /api-reference/admin.yaml delete /api/v3/admin/orders/{order_id}/items/{id} Removes an item from the order. **Required scope:** `write_orders` (for API-key authentication). # Remove store credit from an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/remove-store-credit-from-an-order /api-reference/admin.yaml delete /api/v3/admin/orders/{order_id}/store_credits Removes any applied store credit from the order. **Required scope:** `write_store_credits` (for API-key authentication). # Resend confirmation email Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/resend-confirmation-email /api-reference/admin.yaml post /api/v3/admin/orders/{id}/resend_confirmation Publishes the order.completed event to trigger confirmation email delivery. **Required scope:** `write_orders` (for API-key authentication). # Resume a canceled order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/resume-a-canceled-order /api-reference/admin.yaml patch /api/v3/admin/orders/{id}/resume Resumes a previously canceled order. **Required scope:** `write_orders` (for API-key authentication). # Show an item Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/show-an-item /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/items/{id} Returns details of a specific line item. **Required scope:** `read_orders` (for API-key authentication). # Show an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/show-an-order /api-reference/admin.yaml get /api/v3/admin/orders/{id} Returns full order details including admin-only fields. **Required scope:** `read_orders` (for API-key authentication). # Update an item Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/update-an-item /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/items/{id} Updates an order item quantity or metadata. **Required scope:** `write_orders` (for API-key authentication). # Update an order Source: https://spreecommerce.org/docs/api-reference/admin-api/orders/update-an-order /api-reference/admin.yaml patch /api/v3/admin/orders/{id} Updates an order. Supports updating email, addresses, special instructions, and line items. **Required scope:** `write_orders` (for API-key authentication). # List available payment provider types Source: https://spreecommerce.org/docs/api-reference/admin-api/payment-methods/list-available-payment-provider-types /api-reference/admin.yaml get /api/v3/admin/payment_methods/types Returns the registered Spree::PaymentMethod subclasses that can be used to create new payment methods. Useful for populating a "Provider" dropdown in admin UIs. **Required scope:** `read_settings` (for API-key authentication). # List payment methods Source: https://spreecommerce.org/docs/api-reference/admin-api/payment-methods/list-payment-methods /api-reference/admin.yaml get /api/v3/admin/payment_methods Returns the store's configured payment methods. Use `source_required: true` to know which methods need a saved source. **Required scope:** `read_settings` (for API-key authentication). # Show a payment method Source: https://spreecommerce.org/docs/api-reference/admin-api/payment-methods/show-a-payment-method /api-reference/admin.yaml get /api/v3/admin/payment_methods/{id} Returns a payment method by ID. **Required scope:** `read_settings` (for API-key authentication). # Capture a payment Source: https://spreecommerce.org/docs/api-reference/admin-api/payments/capture-a-payment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/payments/{id}/capture Captures a pending payment. **Required scope:** `write_payments` (for API-key authentication). # Create a payment Source: https://spreecommerce.org/docs/api-reference/admin-api/payments/create-a-payment /api-reference/admin.yaml post /api/v3/admin/orders/{order_id}/payments Creates a new payment for the order. **Required scope:** `write_payments` (for API-key authentication). # List payments Source: https://spreecommerce.org/docs/api-reference/admin-api/payments/list-payments /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/payments Returns all payments for an order. **Required scope:** `read_payments` (for API-key authentication). # Show a payment Source: https://spreecommerce.org/docs/api-reference/admin-api/payments/show-a-payment /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/payments/{id} Returns details of a specific payment. **Required scope:** `read_payments` (for API-key authentication). # Void a payment Source: https://spreecommerce.org/docs/api-reference/admin-api/payments/void-a-payment /api-reference/admin.yaml patch /api/v3/admin/orders/{order_id}/payments/{id}/void Voids a payment. **Required scope:** `write_payments` (for API-key authentication). # Activate a price list Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/activate-a-price-list /api-reference/admin.yaml patch /api/v3/admin/price_lists/{id}/activate Transitions a draft / inactive list to `active`. If `starts_at` is in the future the list is marked `scheduled` instead, matching the legacy admin behaviour. **Required scope:** `write_products` (for API-key authentication). # Bulk-delete prices Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/bulk-delete-prices /api-reference/admin.yaml delete /api/v3/admin/prices/bulk_destroy Soft-deletes each price in `ids`. Returns the count actually destroyed. **Required scope:** `write_products` (for API-key authentication). # Bulk-upsert prices Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/bulk-upsert-prices /api-reference/admin.yaml post /api/v3/admin/prices/bulk_upsert Upserts a batch of prices in a single SQL round trip. Each row either: * targets an existing price by `id`, OR * matches on the unique key `(variant_id, currency, price_list_id)` — updating the existing row if one exists, creating one otherwise. Model callbacks (e.g. PriceHistory) are bypassed; this is a bulk-write fast path for the admin spreadsheet. The response carries `price_count` — the number of rows touched. **Required scope:** `write_products` (for API-key authentication). # Create a price Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/create-a-price /api-reference/admin.yaml post /api/v3/admin/prices Creates a single price. Omit `price_list_id` to create a base price. For more than a handful of rows, prefer `POST /admin/prices/bulk_upsert`. **Required scope:** `write_products` (for API-key authentication). # Create a price list Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/create-a-price-list /api-reference/admin.yaml post /api/v3/admin/price_lists Creates a new draft price list. **Required scope:** `write_products` (for API-key authentication). # Deactivate a price list Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/deactivate-a-price-list /api-reference/admin.yaml patch /api/v3/admin/price_lists/{id}/deactivate **Required scope:** `write_products` (for API-key authentication). # Delete a price Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/delete-a-price /api-reference/admin.yaml delete /api/v3/admin/prices/{id} Soft-deletes the price (acts_as_paranoid). **Required scope:** `write_products` (for API-key authentication). # Delete a price list Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/delete-a-price-list /api-reference/admin.yaml delete /api/v3/admin/price_lists/{id} Soft-deletes the price list. Associated prices are removed asynchronously. **Required scope:** `write_products` (for API-key authentication). # Get a price Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/get-a-price /api-reference/admin.yaml get /api/v3/admin/prices/{id} **Required scope:** `read_products` (for API-key authentication). # Get a price list Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/get-a-price-list /api-reference/admin.yaml get /api/v3/admin/price_lists/{id} **Required scope:** `read_products` (for API-key authentication). # List price lists Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/list-price-lists /api-reference/admin.yaml get /api/v3/admin/price_lists Returns the price lists configured for the current store. **Required scope:** `read_products` (for API-key authentication). # List prices Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/list-prices /api-reference/admin.yaml get /api/v3/admin/prices Generic prices endpoint covering both base prices and price-list overrides. Filter with Ransack: `q[price_list_id_eq]=…`, `q[currency_eq]=USD`, `q[price_list_id_null]=true` (base prices only). The admin spreadsheet uses this with server-side pagination so it scales past the metadata-PATCH path on `/price_lists/:id`. **Required scope:** `read_products` (for API-key authentication). # Update a price Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/update-a-price /api-reference/admin.yaml patch /api/v3/admin/prices/{id} **Required scope:** `write_products` (for API-key authentication). # Update a price list Source: https://spreecommerce.org/docs/api-reference/admin-api/pricing/update-a-price-list /api-reference/admin.yaml patch /api/v3/admin/price_lists/{id} Updates a price list. The optional `rules:` array reconciles nested STI-typed price rules in a single round-trip — existing rules update by `id`, new rules build, missing rules destroy. Mirrors the promotion editor's "save the whole thing on Save" pattern. **Required scope:** `write_products` (for API-key authentication). # Bulk-add products to categories Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-add-products-to-categories /api-reference/admin.yaml post /api/v3/admin/products/bulk_add_to_categories Attaches each product in `ids` to every category (taxon) in `category_ids`. Categories from sibling stores are silently ignored. **Required scope:** `write_products` (for API-key authentication). # Bulk-add products to channels Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-add-products-to-channels /api-reference/admin.yaml post /api/v3/admin/products/bulk_add_to_channels Publishes each product in `ids` on every channel in `channel_ids`. Idempotent — re-publishing on an existing channel is a no-op. Channels from sibling stores are silently ignored. **Required scope:** `write_products` (for API-key authentication). # Bulk-add tags to products Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-add-tags-to-products /api-reference/admin.yaml post /api/v3/admin/products/bulk_add_tags Adds each tag name in `tags` to every product in `ids`. Tags are upserted by name. **Required scope:** `write_products` (for API-key authentication). # Bulk-delete products Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-delete-products /api-reference/admin.yaml delete /api/v3/admin/products/bulk_destroy Soft-deletes each product in `ids`. Returns the count actually destroyed. **Required scope:** `write_products` (for API-key authentication). # Bulk-remove products from categories Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-remove-products-from-categories /api-reference/admin.yaml post /api/v3/admin/products/bulk_remove_from_categories Detaches each product in `ids` from every category in `category_ids`. **Required scope:** `write_products` (for API-key authentication). # Bulk-remove products from channels Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-remove-products-from-channels /api-reference/admin.yaml post /api/v3/admin/products/bulk_remove_from_channels Unpublishes each product in `ids` from every channel in `channel_ids`. **Required scope:** `write_products` (for API-key authentication). # Bulk-remove tags from products Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-remove-tags-from-products /api-reference/admin.yaml post /api/v3/admin/products/bulk_remove_tags Removes each tag in `tags` from every product in `ids`. **Required scope:** `write_products` (for API-key authentication). # Bulk-update product status Source: https://spreecommerce.org/docs/api-reference/admin-api/products/bulk-update-product-status /api-reference/admin.yaml post /api/v3/admin/products/bulk_status_update Sets `status` on each product in `ids`. Reindexes the affected products for search. Cross-store IDs are silently dropped. Returns counts. **Required scope:** `write_products` (for API-key authentication). # Create a product Source: https://spreecommerce.org/docs/api-reference/admin-api/products/create-a-product /api-reference/admin.yaml post /api/v3/admin/products Creates a new product. Supports nested variants with prices and option types. Option types and values are auto-created if they don't exist. Prices are upserted by currency. Stock items are upserted by stock location. **Required scope:** `write_products` (for API-key authentication). # Create a product custom field Source: https://spreecommerce.org/docs/api-reference/admin-api/products/create-a-product-custom-field /api-reference/admin.yaml post /api/v3/admin/products/{product_id}/custom_fields Sets a custom field value on the product. Requires an existing CustomFieldDefinition; pass its prefixed `cfdef_…` id. **Required scope:** `write_products` (for API-key authentication). # Delete a product Source: https://spreecommerce.org/docs/api-reference/admin-api/products/delete-a-product /api-reference/admin.yaml delete /api/v3/admin/products/{id} Soft-deletes a product. **Required scope:** `write_products` (for API-key authentication). # Delete a product custom field Source: https://spreecommerce.org/docs/api-reference/admin-api/products/delete-a-product-custom-field /api-reference/admin.yaml delete /api/v3/admin/products/{product_id}/custom_fields/{id} **Required scope:** `write_products` (for API-key authentication). # Get a product Source: https://spreecommerce.org/docs/api-reference/admin-api/products/get-a-product /api-reference/admin.yaml get /api/v3/admin/products/{id} Returns a single product by ID. **Required scope:** `read_products` (for API-key authentication). # List product custom fields Source: https://spreecommerce.org/docs/api-reference/admin-api/products/list-product-custom-fields /api-reference/admin.yaml get /api/v3/admin/products/{product_id}/custom_fields Returns the product's custom field values. **Required scope:** `read_products` (for API-key authentication). # List products Source: https://spreecommerce.org/docs/api-reference/admin-api/products/list-products /api-reference/admin.yaml get /api/v3/admin/products Returns a paginated list of products for the current store. **Required scope:** `read_products` (for API-key authentication). # Show a product custom field Source: https://spreecommerce.org/docs/api-reference/admin-api/products/show-a-product-custom-field /api-reference/admin.yaml get /api/v3/admin/products/{product_id}/custom_fields/{id} **Required scope:** `read_products` (for API-key authentication). # Update a product Source: https://spreecommerce.org/docs/api-reference/admin-api/products/update-a-product /api-reference/admin.yaml patch /api/v3/admin/products/{id} Updates a product. Only provided fields are updated. **Required scope:** `write_products` (for API-key authentication). # Update a product custom field Source: https://spreecommerce.org/docs/api-reference/admin-api/products/update-a-product-custom-field /api-reference/admin.yaml patch /api/v3/admin/products/{product_id}/custom_fields/{id} Updates the custom field's `value`. The linked definition cannot be changed — delete and recreate to switch. **Required scope:** `write_products` (for API-key authentication). # Create a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/create-a-promotion /api-reference/admin.yaml post /api/v3/admin/promotions Creates a new promotion. `code` is required for single-code coupon promotions; pass `multi_codes: true` with `number_of_codes` to auto-generate a batch. Rules and actions can be created in the same request by passing arrays of `{ type, preferences, ... }` rows. The server reconciles to the desired set: new rows are built, existing rows (by `id`) are updated, omitted rows are removed. **Required scope:** `write_promotions` (for API-key authentication). # Create a rule on a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/create-a-rule-on-a-promotion /api-reference/admin.yaml post /api/v3/admin/promotions/{promotion_id}/promotion_rules Adds a new rule to a promotion. The `type` is the wire shorthand from `GET /promotion_rules/types` (e.g. `currency`, `item_total`, `product`). Fully-qualified Ruby class names are also accepted for backward compatibility. **Required scope:** `write_promotions` (for API-key authentication). # Create an action on a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/create-an-action-on-a-promotion /api-reference/admin.yaml post /api/v3/admin/promotions/{promotion_id}/promotion_actions Adds a new action to a promotion. The `type` is the wire shorthand from `GET /promotion_actions/types` (e.g. `free_shipping`, `create_item_adjustments`). Fully-qualified Ruby class names are also accepted for backward compatibility. **Required scope:** `write_promotions` (for API-key authentication). # Delete a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/delete-a-promotion /api-reference/admin.yaml delete /api/v3/admin/promotions/{id} **Required scope:** `write_promotions` (for API-key authentication). # Delete a rule from a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/delete-a-rule-from-a-promotion /api-reference/admin.yaml delete /api/v3/admin/promotions/{promotion_id}/promotion_rules/{id} **Required scope:** `write_promotions` (for API-key authentication). # Delete an action from a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/delete-an-action-from-a-promotion /api-reference/admin.yaml delete /api/v3/admin/promotions/{promotion_id}/promotion_actions/{id} **Required scope:** `write_promotions` (for API-key authentication). # List actions for a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/list-actions-for-a-promotion /api-reference/admin.yaml get /api/v3/admin/promotions/{promotion_id}/promotion_actions **Required scope:** `read_promotions` (for API-key authentication). # List available promotion action types Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/list-available-promotion-action-types /api-reference/admin.yaml get /api/v3/admin/promotion_actions/types Returns the registered Spree::PromotionAction subclasses with their preference schemas. Used by admin UIs to populate the "Add action" picker and render generic preference forms. **Required scope:** `read_promotions` (for API-key authentication). # List available promotion rule types Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/list-available-promotion-rule-types /api-reference/admin.yaml get /api/v3/admin/promotion_rules/types Returns the registered Spree::PromotionRule subclasses with their preference schemas. **Required scope:** `read_promotions` (for API-key authentication). # List coupon codes for a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/list-coupon-codes-for-a-promotion /api-reference/admin.yaml get /api/v3/admin/promotions/{promotion_id}/coupon_codes Returns the auto-generated coupon codes for a multi-code promotion. Single-code promotions store the code on the promotion itself; this endpoint returns an empty list for them. **Required scope:** `read_promotions` (for API-key authentication). # List promotions Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/list-promotions /api-reference/admin.yaml get /api/v3/admin/promotions Returns the store's promotions, including manual coupon and automatic promotions. **Required scope:** `read_promotions` (for API-key authentication). # List rules for a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/list-rules-for-a-promotion /api-reference/admin.yaml get /api/v3/admin/promotions/{promotion_id}/promotion_rules **Required scope:** `read_promotions` (for API-key authentication). # Show a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/show-a-promotion /api-reference/admin.yaml get /api/v3/admin/promotions/{id} **Required scope:** `read_promotions` (for API-key authentication). # Update a promotion Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/update-a-promotion /api-reference/admin.yaml patch /api/v3/admin/promotions/{id} Updates a promotion. The `rules` and `actions` arrays are treated as a *desired set* — rows with `id` update in place, rows without `id` are built fresh, and any existing row not present in the array is destroyed. Pass `rules: []` or `actions: []` to clear them. **Required scope:** `write_promotions` (for API-key authentication). # Update a rule's preferences Source: https://spreecommerce.org/docs/api-reference/admin-api/promotions/update-a-rules-preferences /api-reference/admin.yaml patch /api/v3/admin/promotions/{promotion_id}/promotion_rules/{id} **Required scope:** `write_promotions` (for API-key authentication). # Filter, sort, paginate, and expand Admin API responses Source: https://spreecommerce.org/docs/api-reference/admin-api/querying Query Spree Admin API list endpoints with Ransack filters, sorting, pagination parameters, and the expand option to include related resources. The Admin API uses [Ransack](https://activerecord-hackery.github.io/ransack/) for filtering and sorting on collection endpoints. All filter conditions go through the `q` parameter; sorting and pagination are top-level params. ## Filtering Pass filter conditions via `q`: ```typescript SDK theme={"theme":"night-owl"} const orders = await client.orders.list({ status_eq: 'complete', total_gteq: 100, email_cont: '@example.com', }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ --data-urlencode 'q[status_eq]=complete' \ --data-urlencode 'q[total_gteq]=100' \ --data-urlencode 'q[email_cont]=@example.com' ``` The SDK automatically wraps filter keys in `q[...]` and appends `[]` for array values — pass flat params. ### Common predicates | Predicate | Description | SDK | cURL | | ------------------- | ------------------------------------ | ------------------------------------- | ------------------------------------------------- | | `eq` | Equals | `status_eq: 'complete'` | `q[status_eq]=complete` | | `not_eq` | Not equals | `status_not_eq: 'canceled'` | `q[status_not_eq]=canceled` | | `cont` | Contains (case-insensitive) | `email_cont: '@acme'` | `q[email_cont]=@acme` | | `start` | Starts with | `number_start: 'R10'` | `q[number_start]=R10` | | `end` | Ends with | `slug_end: 'sale'` | `q[slug_end]=sale` | | `lt` / `lteq` | Less than / less than or equal | `total_lteq: 50` | `q[total_lteq]=50` | | `gt` / `gteq` | Greater than / greater than or equal | `total_gteq: 100` | `q[total_gteq]=100` | | `in` | In a list | `status_in: ['complete', 'canceled']` | `q[status_in][]=complete&q[status_in][]=canceled` | | `null` / `not_null` | Is null / not null | `completed_at_not_null: true` | `q[completed_at_not_null]=true` | | `true` / `false` | Boolean | `accepts_email_marketing_true: 1` | `q[accepts_email_marketing_true]=1` | ### Prefixed IDs in filters Resource ID filters accept Stripe-style prefixed IDs directly. The server decodes them before querying: ```typescript SDK theme={"theme":"night-owl"} // All orders for a specific customer const orders = await client.orders.list({ user_id_eq: 'cus_UkLWZg9DAJ', }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ --data-urlencode 'q[user_id_eq]=cus_UkLWZg9DAJ' ``` The same applies to `_id_in`, `_id_not_eq`, and other ID predicates. ### Combining filters Multiple filters combine with AND: ```typescript SDK theme={"theme":"night-owl"} // Completed orders over $100 from the last 7 days const orders = await client.orders.list({ status_eq: 'complete', total_gteq: 100, completed_at_gteq: new Date(Date.now() - 7 * 86_400_000).toISOString(), }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ --data-urlencode 'q[status_eq]=complete' \ --data-urlencode 'q[total_gteq]=100' \ --data-urlencode 'q[completed_at_gteq]=2026-04-22T00:00:00Z' ``` ### Filtering by association Use underscore notation to filter on associated model attributes: ```typescript SDK theme={"theme":"night-owl"} // Customers tagged as "wholesale" const customers = await client.customers.list({ tags_name_eq: 'wholesale', }) // Products in a specific category const products = await client.products.list({ taxons_id_eq: 'ctg_xxx', }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/customers' \ -H 'X-Spree-Api-Key: sk_xxx' \ --data-urlencode 'q[tags_name_eq]=wholesale' ``` ### Custom search scopes Some resources expose convenience search scopes: | Resource | Scope | Example | | --------- | ---------------------- | -------------------------------------- | | Customers | `search` | Full-text over email + first/last name | | Customers | `with_min_total_spent` | Filter by lifetime spend | | Orders | `multi_search` | Number + email full-text | ```typescript SDK theme={"theme":"night-owl"} const customers = await client.customers.list({ search: 'jane', }) const vipCustomers = await client.customers.list({ with_min_total_spent: 1000, }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/customers' \ -H 'X-Spree-Api-Key: sk_xxx' \ --data-urlencode 'q[search]=jane' ``` ## Sorting Use the top-level `sort` parameter on any list endpoint. Prefix with `-` for descending. Follows the [JSON:API sorting convention](https://jsonapi.org/format/#fetching-sorting). ```typescript SDK theme={"theme":"night-owl"} // Most recent orders first const orders = await client.orders.list({ sort: '-completed_at', }) // Multiple sort keys (priority left to right) const customers = await client.customers.list({ sort: '-total_spent,email', }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ -d 'sort=-completed_at' ``` Sortable columns are limited to those whitelisted on the model (Ransack's `whitelisted_ransackable_attributes`). Sorting on a virtual column (e.g., a serializer-computed field like `display_total_spent`) is not supported. ## Pagination All collection endpoints return paginated results. Control with `page` and `limit`: ```typescript SDK theme={"theme":"night-owl"} const { data: orders, meta } = await client.orders.list({ page: 2, limit: 50, }) // meta.count, meta.pages, meta.previous, meta.next ... ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ -d 'page=2' \ -d 'limit=50' ``` | Parameter | Default | Max | Description | | --------- | ------- | ----- | ----------------------- | | `page` | `1` | — | Page number (1-indexed) | | `limit` | `25` | `100` | Records per page | ### Pagination metadata Responses include a `meta` object: ```json theme={"theme":"night-owl"} { "data": [...], "meta": { "page": 2, "limit": 50, "count": 327, "pages": 7, "from": 51, "to": 100, "in": 50, "previous": 1, "next": 3 } } ``` | Field | Description | | -------------------- | ------------------------------------ | | `page` | Current page number | | `limit` | Records per page | | `count` | Total number of matching records | | `pages` | Total number of pages | | `from` / `to` / `in` | Position range / count of this page | | `previous` / `next` | Previous/next page number, or `null` | ## Expanding associations Most admin endpoints return slim payloads by default — associations are returned as IDs. Use the `expand` parameter to include related resources inline: ```typescript SDK theme={"theme":"night-owl"} const order = await client.orders.get('or_xxx', { expand: ['items', 'fulfillments', 'payments', 'customer'], }) // Nested expand const customer = await client.customers.get('cus_xxx', { expand: ['addresses', 'store_credits'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders/or_xxx' \ -H 'X-Spree-Api-Key: sk_xxx' \ -d 'expand=items,fulfillments,payments,customer' ``` ### Nested expand Use dot notation up to 4 levels deep: ```typescript theme={"theme":"night-owl"} const order = await client.orders.get('or_xxx', { expand: ['items.variant.product', 'fulfillments.tracking'], }) ``` A nested expand implicitly includes its parent — `expand: ['items.variant']` returns both `items` and their `variant` data. ### What can I expand? Each endpoint documents its supported expand keys in the OpenAPI reference. Common admin expansions: | Resource | Common expands | | --------- | ------------------------------------------------------------------------------------------------------------------ | | Orders | `items`, `fulfillments`, `payments`, `customer`, `discounts`, `adjustments`, `billing_address`, `shipping_address` | | Customers | `addresses`, `store_credits`, `orders` | | Products | `variants`, `media`, `option_types`, `categories` | ## Field selection Use the `fields` parameter to request only specific fields on a resource. Reduces payload size for bandwidth-sensitive integrations: ```typescript SDK theme={"theme":"night-owl"} const orders = await client.orders.list({ fields: ['number', 'total', 'status', 'completed_at'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'https://store.example.com/api/v3/admin/orders' \ -H 'X-Spree-Api-Key: sk_xxx' \ -d 'fields=number,total,status,completed_at' ``` Rules: * `id` is always included * Expanded associations return their full payload regardless of `fields` * Field selection applies to the top-level resource only TypeScript types in `@spree/admin-sdk` remain fully typed regardless of `fields`. At runtime, only the requested fields are present — the rest are `undefined`. # Create a refund Source: https://spreecommerce.org/docs/api-reference/admin-api/refunds/create-a-refund /api-reference/admin.yaml post /api/v3/admin/orders/{order_id}/refunds Creates a refund for a payment on the order. The refund is automatically processed via the payment gateway. **Required scope:** `write_refunds` (for API-key authentication). # List refunds Source: https://spreecommerce.org/docs/api-reference/admin-api/refunds/list-refunds /api-reference/admin.yaml get /api/v3/admin/orders/{order_id}/refunds Returns all refunds for an order. **Required scope:** `read_refunds` (for API-key authentication). # Get a store credit category Source: https://spreecommerce.org/docs/api-reference/admin-api/settings/get-a-store-credit-category /api-reference/admin.yaml get /api/v3/admin/store_credit_categories/{id} Returns a single store credit category by prefixed ID. **Required scope:** `read_settings` (for API-key authentication). # Get the current store Source: https://spreecommerce.org/docs/api-reference/admin-api/settings/get-the-current-store /api-reference/admin.yaml get /api/v3/admin/store Returns the current store configuration. The store is resolved from the request context (host or admin selection); there is no `id` parameter. **Required scope:** `read_settings` (for API-key authentication). # List store credit categories Source: https://spreecommerce.org/docs/api-reference/admin-api/settings/list-store-credit-categories /api-reference/admin.yaml get /api/v3/admin/store_credit_categories Returns the configured store credit categories. Categories classify store credits (e.g., "Goodwill", "Gift Card", "Refund") and surface in the admin UI as a dropdown when issuing or editing a store credit. Category names matching `Spree::Config[:non_expiring_credit_types]` are flagged via `non_expiring: true`. **Required scope:** `read_settings` (for API-key authentication). # List tags Source: https://spreecommerce.org/docs/api-reference/admin-api/settings/list-tags /api-reference/admin.yaml get /api/v3/admin/tags Returns tag names for a given taggable type. Used for autocomplete in tag inputs on products, orders, and customers. # Update the current store Source: https://spreecommerce.org/docs/api-reference/admin-api/settings/update-the-current-store /api-reference/admin.yaml patch /api/v3/admin/store Updates the current store configuration. **Required scope:** `write_settings` (for API-key authentication). # Create an invitation Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/create-an-invitation /api-reference/admin.yaml post /api/v3/admin/invitations Invites a teammate by email. The invitation is scoped to the current store and carries the chosen role; on accept, a `RoleUser` is created via the invitation's `after_accept` callback. **Required scope:** `write_settings` (for API-key authentication). # List invitations Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/list-invitations /api-reference/admin.yaml get /api/v3/admin/invitations Returns invitations for the current store, including pending and accepted. **Required scope:** `read_settings` (for API-key authentication). # List roles Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/list-roles /api-reference/admin.yaml get /api/v3/admin/roles Returns the roles available for staff role pickers. Roles are global, not per-store. **Required scope:** `read_settings` (for API-key authentication). # List staff Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/list-staff /api-reference/admin.yaml get /api/v3/admin/admin_users Returns admin users with at least one role assignment on the current store. **Required scope:** `read_settings` (for API-key authentication). # Remove a staff member from this store Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/remove-a-staff-member-from-this-store /api-reference/admin.yaml delete /api/v3/admin/admin_users/{id} Removes the user's role assignments on the current store. The account is preserved — the user keeps access to any other stores. **Required scope:** `write_settings` (for API-key authentication). # Resend an invitation Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/resend-an-invitation /api-reference/admin.yaml patch /api/v3/admin/invitations/{id}/resend Issues a fresh token and dispatches the invitation email again. **Required scope:** `write_settings` (for API-key authentication). # Revoke an invitation Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/revoke-an-invitation /api-reference/admin.yaml delete /api/v3/admin/invitations/{id} **Required scope:** `write_settings` (for API-key authentication). # Show a staff member Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/show-a-staff-member /api-reference/admin.yaml get /api/v3/admin/admin_users/{id} **Required scope:** `read_settings` (for API-key authentication). # Update a staff member Source: https://spreecommerce.org/docs/api-reference/admin-api/staff/update-a-staff-member /api-reference/admin.yaml patch /api/v3/admin/admin_users/{id} Updates name fields and reassigns roles for the current store. `role_ids` is a complete replacement — roles not in the array are removed for this store. **Required scope:** `write_settings` (for API-key authentication). # Create a stock location Source: https://spreecommerce.org/docs/api-reference/admin-api/stock-locations/create-a-stock-location /api-reference/admin.yaml post /api/v3/admin/stock_locations Creates a new stock location. Setting `default: true` automatically demotes the previous default location. **Required scope:** `write_settings` (for API-key authentication). # Delete a stock location Source: https://spreecommerce.org/docs/api-reference/admin-api/stock-locations/delete-a-stock-location /api-reference/admin.yaml delete /api/v3/admin/stock_locations/{id} Soft-deletes the stock location (sets `deleted_at`). Existing fulfillments that referenced it keep the historical record via `Spree::StockLocation.with_deleted`. **Required scope:** `write_settings` (for API-key authentication). # Get a stock location Source: https://spreecommerce.org/docs/api-reference/admin-api/stock-locations/get-a-stock-location /api-reference/admin.yaml get /api/v3/admin/stock_locations/{id} Returns a single stock location by prefixed ID. **Required scope:** `read_settings` (for API-key authentication). # List stock locations Source: https://spreecommerce.org/docs/api-reference/admin-api/stock-locations/list-stock-locations /api-reference/admin.yaml get /api/v3/admin/stock_locations Returns the configured stock locations. Stock locations are global (shared across stores). Filter with Ransack predicates such as `q[active_eq]`, `q[kind_eq]`, `q[pickup_enabled_eq]`, or `q[name_cont]`. Pickup-related attributes (`kind`, `pickup_enabled`, `pickup_stock_policy`, `pickup_ready_in_minutes`, `pickup_instructions`) drive merchant pickup support at checkout — customers can collect orders from any active location with `pickup_enabled: true`. **Required scope:** `read_settings` (for API-key authentication). # Update a stock location Source: https://spreecommerce.org/docs/api-reference/admin-api/stock-locations/update-a-stock-location /api-reference/admin.yaml patch /api/v3/admin/stock_locations/{id} Updates an existing stock location. Same address-field conventions as the create endpoint. Setting `default: true` automatically demotes the previous default. **Required scope:** `write_settings` (for API-key authentication). # Create a variant Source: https://spreecommerce.org/docs/api-reference/admin-api/variants/create-a-variant /api-reference/admin.yaml post /api/v3/admin/products/{product_id}/variants Creates a new variant for a product. Supports nested prices and stock items. Option types and values are auto-created if they don't exist. Prices are upserted by currency. Stock items are upserted by stock location. **Required scope:** `write_products` (for API-key authentication). # Delete a variant Source: https://spreecommerce.org/docs/api-reference/admin-api/variants/delete-a-variant /api-reference/admin.yaml delete /api/v3/admin/products/{product_id}/variants/{id} Soft-deletes a variant. **Required scope:** `write_products` (for API-key authentication). # Get a variant Source: https://spreecommerce.org/docs/api-reference/admin-api/variants/get-a-variant /api-reference/admin.yaml get /api/v3/admin/products/{product_id}/variants/{id} Returns a single variant by ID. **Required scope:** `read_products` (for API-key authentication). # List product variants Source: https://spreecommerce.org/docs/api-reference/admin-api/variants/list-product-variants /api-reference/admin.yaml get /api/v3/admin/products/{product_id}/variants Returns a paginated list of variants for a product, including the master variant. **Required scope:** `read_products` (for API-key authentication). # Update a variant Source: https://spreecommerce.org/docs/api-reference/admin-api/variants/update-a-variant /api-reference/admin.yaml patch /api/v3/admin/products/{product_id}/variants/{id} Updates a variant. Only provided fields are updated. **Required scope:** `write_products` (for API-key authentication). # Create a webhook endpoint Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/create-a-webhook-endpoint /api-reference/admin.yaml post /api/v3/admin/webhook_endpoints Creates a new outbound webhook subscription. The plaintext `secret_key` is returned **once** in this response — persist it immediately to verify incoming webhook signatures. Subsequent reads return `null` for the secret. Pass an empty `subscriptions` array or omit it to receive every event. **Required scope:** `write_settings` (for API-key authentication). # Delete a webhook endpoint Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/delete-a-webhook-endpoint /api-reference/admin.yaml delete /api/v3/admin/webhook_endpoints/{id} Soft-deletes the endpoint and stops future deliveries. **Required scope:** `write_settings` (for API-key authentication). # Disable a webhook endpoint Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/disable-a-webhook-endpoint /api-reference/admin.yaml patch /api/v3/admin/webhook_endpoints/{id}/disable Manually pauses an endpoint. Unlike auto-disable (triggered after repeated delivery failures), no notification email is sent. **Required scope:** `write_settings` (for API-key authentication). # Get a webhook delivery Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/get-a-webhook-delivery /api-reference/admin.yaml get /api/v3/admin/webhook_endpoints/{webhook_endpoint_id}/deliveries/{id} Returns a single delivery attempt with the full request payload and the response body the receiver returned. Use this for ad-hoc debug of failed deliveries. **Required scope:** `read_settings` (for API-key authentication). # Get a webhook endpoint Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/get-a-webhook-endpoint /api-reference/admin.yaml get /api/v3/admin/webhook_endpoints/{id} Returns a single webhook endpoint by prefixed ID. **Required scope:** `read_settings` (for API-key authentication). # List webhook deliveries Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/list-webhook-deliveries /api-reference/admin.yaml get /api/v3/admin/webhook_endpoints/{webhook_endpoint_id}/deliveries Returns delivery attempts for the given endpoint, most recent first. Each row carries the original request payload, the response code (when the receiver replied), the execution time, and any transport error — everything needed to audit failures and decide whether to redeliver. **Required scope:** `read_settings` (for API-key authentication). # List webhook endpoints Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/list-webhook-endpoints /api-reference/admin.yaml get /api/v3/admin/webhook_endpoints Returns outbound webhook subscriptions for the current store. Each endpoint receives a signed POST when any subscribed event fires. `secret_key` is `null` on list reads — the plaintext is delivered exactly once on create. **Required scope:** `read_settings` (for API-key authentication). # Re-enable a webhook endpoint Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/re-enable-a-webhook-endpoint /api-reference/admin.yaml patch /api/v3/admin/webhook_endpoints/{id}/enable Re-enables an endpoint that was manually or automatically disabled. **Required scope:** `write_settings` (for API-key authentication). # Redeliver a webhook delivery Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/redeliver-a-webhook-delivery /api-reference/admin.yaml post /api/v3/admin/webhook_endpoints/{webhook_endpoint_id}/deliveries/{id}/redeliver Creates a new delivery row with the same payload + event_name and queues it. The original row is preserved for audit history. **Required scope:** `write_settings` (for API-key authentication). # Send a test delivery Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/send-a-test-delivery /api-reference/admin.yaml post /api/v3/admin/webhook_endpoints/{id}/send_test Creates a `webhook.test` delivery record and queues it. Use this to verify the endpoint is reachable and your signature verification accepts Spree's payloads. **Required scope:** `write_settings` (for API-key authentication). # Update a webhook endpoint Source: https://spreecommerce.org/docs/api-reference/admin-api/webhooks/update-a-webhook-endpoint /api-reference/admin.yaml patch /api/v3/admin/webhook_endpoints/{id} Updates name, URL, active flag, or the event subscription list. Toggling `active` here is equivalent to calling `disable`/`enable` without an audit reason. **Required scope:** `write_settings` (for API-key authentication). # Create an Address Source: https://spreecommerce.org/docs/api-reference/platform/addresses/create-an-address /api-reference/platform.yaml post /api/v2/platform/addresses Creates an Address # Delete an Address Source: https://spreecommerce.org/docs/api-reference/platform/addresses/delete-an-address /api-reference/platform.yaml delete /api/v2/platform/addresses/{id} Deletes an Address # Return a list of Addresses Source: https://spreecommerce.org/docs/api-reference/platform/addresses/return-a-list-of-addresses /api-reference/platform.yaml get /api/v2/platform/addresses Returns a list of Addresses # Return an Address Source: https://spreecommerce.org/docs/api-reference/platform/addresses/return-an-address /api-reference/platform.yaml get /api/v2/platform/addresses/{id} Returns an Address # Update an Address Source: https://spreecommerce.org/docs/api-reference/platform/addresses/update-an-address /api-reference/platform.yaml patch /api/v2/platform/addresses/{id} Updates an Address # Create an Adjustment Source: https://spreecommerce.org/docs/api-reference/platform/adjustments/create-an-adjustment /api-reference/platform.yaml post /api/v2/platform/adjustments Creates an Adjustment # Delete an Adjustment Source: https://spreecommerce.org/docs/api-reference/platform/adjustments/delete-an-adjustment /api-reference/platform.yaml delete /api/v2/platform/adjustments/{id} Deletes an Adjustment # Return a list of Adjustments Source: https://spreecommerce.org/docs/api-reference/platform/adjustments/return-a-list-of-adjustments /api-reference/platform.yaml get /api/v2/platform/adjustments Returns a list of Adjustments # Return an Adjustment Source: https://spreecommerce.org/docs/api-reference/platform/adjustments/return-an-adjustment /api-reference/platform.yaml get /api/v2/platform/adjustments/{id} Returns an Adjustment # Update an Adjustment Source: https://spreecommerce.org/docs/api-reference/platform/adjustments/update-an-adjustment /api-reference/platform.yaml patch /api/v2/platform/adjustments/{id} Updates an Adjustment # Authentication Source: https://spreecommerce.org/docs/api-reference/platform/authentication 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. ### Creating an application inside Spree To generate a valid token for the Platform API, you'll first need to create an application inside Spree. 1. Go to Spree's admin panel and open `Apps` -> `oAuth Applications` in the menu 2. Click on `New oAuth Application` 3. Give your application a name and click `Create` 4. Save your Client ID and Secret - you'll later use it to generate an OAuth token to access the platform API ### Generating OAuth token 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. To obtain the token, send the following `POST` request to `/spree_oauth/token` ```json theme={"theme":"night-owl"} { "grant_type": "client_credentials", "client_id": "xxx", "client_secret": "xxx", "scope": "admin" } ``` In the response, you'll receive a token to pass in `Authorization: Bearer {token}` header when making requests to the Platform API. # Create a Classification Source: https://spreecommerce.org/docs/api-reference/platform/classifications/create-a-classification /api-reference/platform.yaml post /api/v2/platform/classifications Creates a Classification # Delete a Classification Source: https://spreecommerce.org/docs/api-reference/platform/classifications/delete-a-classification /api-reference/platform.yaml delete /api/v2/platform/classifications/{id} Deletes a Classification # Return a Classification Source: https://spreecommerce.org/docs/api-reference/platform/classifications/return-a-classification /api-reference/platform.yaml get /api/v2/platform/classifications/{id} Returns a Classification # Return a list of Classifications Source: https://spreecommerce.org/docs/api-reference/platform/classifications/return-a-list-of-classifications /api-reference/platform.yaml get /api/v2/platform/classifications Returns a list of Classifications # Update a Classification Source: https://spreecommerce.org/docs/api-reference/platform/classifications/update-a-classification /api-reference/platform.yaml patch /api/v2/platform/classifications/{id} Updates a Classification # Create a CMS Page Source: https://spreecommerce.org/docs/api-reference/platform/cms-pages/create-a-cms-page /api-reference/platform.yaml post /api/v2/platform/cms_pages Creates a CMS Page # Delete a CMS Page Source: https://spreecommerce.org/docs/api-reference/platform/cms-pages/delete-a-cms-page /api-reference/platform.yaml delete /api/v2/platform/cms_pages/{id} Deletes a CMS Page # Return a CMS Page Source: https://spreecommerce.org/docs/api-reference/platform/cms-pages/return-a-cms-page /api-reference/platform.yaml get /api/v2/platform/cms_pages/{id} Returns a CMS Page # Return a list of CMS Pages Source: https://spreecommerce.org/docs/api-reference/platform/cms-pages/return-a-list-of-cms-pages /api-reference/platform.yaml get /api/v2/platform/cms_pages Returns a list of CMS Pages # Update a CMS Page Source: https://spreecommerce.org/docs/api-reference/platform/cms-pages/update-a-cms-page /api-reference/platform.yaml patch /api/v2/platform/cms_pages/{id} Updates a CMS Page # Create a CMS Section Source: https://spreecommerce.org/docs/api-reference/platform/cms-sections/create-a-cms-section /api-reference/platform.yaml post /api/v2/platform/cms_sections Creates a CMS Section # Delete a CMS Section Source: https://spreecommerce.org/docs/api-reference/platform/cms-sections/delete-a-cms-section /api-reference/platform.yaml delete /api/v2/platform/cms_sections/{id} Deletes a CMS Section # Return a CMS Section Source: https://spreecommerce.org/docs/api-reference/platform/cms-sections/return-a-cms-section /api-reference/platform.yaml get /api/v2/platform/cms_sections/{id} Returns a CMS Section # Return a list of CMS Sections Source: https://spreecommerce.org/docs/api-reference/platform/cms-sections/return-a-list-of-cms-sections /api-reference/platform.yaml get /api/v2/platform/cms_sections Returns a list of CMS Sections # Update a CMS Section Source: https://spreecommerce.org/docs/api-reference/platform/cms-sections/update-a-cms-section /api-reference/platform.yaml patch /api/v2/platform/cms_sections/{id} Updates a CMS Section # Returns a Country Source: https://spreecommerce.org/docs/api-reference/platform/countries/returns-a-country /api-reference/platform.yaml get /api/v2/platform/countries/{id} Returns a Country # Returns a list of Countries Source: https://spreecommerce.org/docs/api-reference/platform/countries/returns-a-list-of-countries /api-reference/platform.yaml get /api/v2/platform/countries Returns a list of Countries # Create a Data Feed Source: https://spreecommerce.org/docs/api-reference/platform/data-feeds/create-a-data-feed /api-reference/platform.yaml post /api/v2/platform/data_feeds Creates a Data Feed # Delete a Data Feed Source: https://spreecommerce.org/docs/api-reference/platform/data-feeds/delete-a-data-feed /api-reference/platform.yaml delete /api/v2/platform/data_feeds/{id} Deletes a Data Feed # Return a Data Feed Source: https://spreecommerce.org/docs/api-reference/platform/data-feeds/return-a-data-feed /api-reference/platform.yaml get /api/v2/platform/data_feeds/{id} Returns a Data Feed # Return a list of Data Feeds Source: https://spreecommerce.org/docs/api-reference/platform/data-feeds/return-a-list-of-data-feeds /api-reference/platform.yaml get /api/v2/platform/data_feeds # Update a Data Feed Source: https://spreecommerce.org/docs/api-reference/platform/data-feeds/update-a-data-feed /api-reference/platform.yaml patch /api/v2/platform/data_feeds/{id} Updates a Data Feed # Create a Digital Asset Source: https://spreecommerce.org/docs/api-reference/platform/digital-assets/create-a-digital-asset /api-reference/platform.yaml post /api/v2/platform/digitals Creates a Digital Asset # Delete a Digital Asset Source: https://spreecommerce.org/docs/api-reference/platform/digital-assets/delete-a-digital-asset /api-reference/platform.yaml delete /api/v2/platform/digitals/{id} Deletes a Digital Asset # Return a Digital Asset Source: https://spreecommerce.org/docs/api-reference/platform/digital-assets/return-a-digital-asset /api-reference/platform.yaml get /api/v2/platform/digitals/{id} Returns a Digital Asset # Return a list of Digital Assets Source: https://spreecommerce.org/docs/api-reference/platform/digital-assets/return-a-list-of-digital-assets /api-reference/platform.yaml get /api/v2/platform/digitals Returns a list of Digital Assets # Update a Digital Asset Source: https://spreecommerce.org/docs/api-reference/platform/digital-assets/update-a-digital-asset /api-reference/platform.yaml patch /api/v2/platform/digitals/{id} Updates a Digital Asset # Create a Digital Link Source: https://spreecommerce.org/docs/api-reference/platform/digital-links/create-a-digital-link /api-reference/platform.yaml post /api/v2/platform/digital_links Creates a Digital Link # Delete a Digital Link Source: https://spreecommerce.org/docs/api-reference/platform/digital-links/delete-a-digital-link /api-reference/platform.yaml delete /api/v2/platform/digital_links/{id} Deletes a Digital Link # Reset a Digital Link Source: https://spreecommerce.org/docs/api-reference/platform/digital-links/reset-a-digital-link /api-reference/platform.yaml patch /api/v2/platform/digital_links/{id}/reset Resets a digital link, allowing further downloads. # Return a Digital Link Source: https://spreecommerce.org/docs/api-reference/platform/digital-links/return-a-digital-link /api-reference/platform.yaml get /api/v2/platform/digital_links/{id} Returns a Digital Link # Return a list of Digital Links Source: https://spreecommerce.org/docs/api-reference/platform/digital-links/return-a-list-of-digital-links /api-reference/platform.yaml get /api/v2/platform/digital_links Returns a list of Digital Links # Update a Digital Link Source: https://spreecommerce.org/docs/api-reference/platform/digital-links/update-a-digital-link /api-reference/platform.yaml patch /api/v2/platform/digital_links/{id} Updates a Digital Link # Create a Line Item Source: https://spreecommerce.org/docs/api-reference/platform/line-items/create-a-line-item /api-reference/platform.yaml post /api/v2/platform/line_items Creates a Line Item # Delete a Line Item Source: https://spreecommerce.org/docs/api-reference/platform/line-items/delete-a-line-item /api-reference/platform.yaml delete /api/v2/platform/line_items/{id} Deletes a Line Item # Return a Line Item Source: https://spreecommerce.org/docs/api-reference/platform/line-items/return-a-line-item /api-reference/platform.yaml get /api/v2/platform/line_items/{id} Returns a Line Item # Return a list of Line Items Source: https://spreecommerce.org/docs/api-reference/platform/line-items/return-a-list-of-line-items /api-reference/platform.yaml get /api/v2/platform/line_items Returns a list of Line Items # Update a Line Item Source: https://spreecommerce.org/docs/api-reference/platform/line-items/update-a-line-item /api-reference/platform.yaml patch /api/v2/platform/line_items/{id} Updates a Line Item # Create a Menu Item Source: https://spreecommerce.org/docs/api-reference/platform/menu-items/create-a-menu-item /api-reference/platform.yaml post /api/v2/platform/menu_items Creates a Menu Item # Delete a Menu Item Source: https://spreecommerce.org/docs/api-reference/platform/menu-items/delete-a-menu-item /api-reference/platform.yaml delete /api/v2/platform/menu_items/{id} Deletes a Menu Item # Reposition a Menu Item Source: https://spreecommerce.org/docs/api-reference/platform/menu-items/reposition-a-menu-item /api-reference/platform.yaml patch /api/v2/platform/menu_items/{id}/reposition Reposition a Menu Item # Return a list of Menu Items Source: https://spreecommerce.org/docs/api-reference/platform/menu-items/return-a-list-of-menu-items /api-reference/platform.yaml get /api/v2/platform/menu_items Returns a list of Menu Items # Return a Menu Item Source: https://spreecommerce.org/docs/api-reference/platform/menu-items/return-a-menu-item /api-reference/platform.yaml get /api/v2/platform/menu_items/{id} Returns a Menu Item # Update a Menu Item Source: https://spreecommerce.org/docs/api-reference/platform/menu-items/update-a-menu-item /api-reference/platform.yaml patch /api/v2/platform/menu_items/{id} Updates a Menu Item # Create a Menu Source: https://spreecommerce.org/docs/api-reference/platform/menus/create-a-menu /api-reference/platform.yaml post /api/v2/platform/menus Creates a Menu # Delete a Menu Source: https://spreecommerce.org/docs/api-reference/platform/menus/delete-a-menu /api-reference/platform.yaml delete /api/v2/platform/menus/{id} Deletes a Menu # Return a list of Menus Source: https://spreecommerce.org/docs/api-reference/platform/menus/return-a-list-of-menus /api-reference/platform.yaml get /api/v2/platform/menus Returns a list of Menus # Return a Menu Source: https://spreecommerce.org/docs/api-reference/platform/menus/return-a-menu /api-reference/platform.yaml get /api/v2/platform/menus/{id} Returns a Menu # Update a Menu Source: https://spreecommerce.org/docs/api-reference/platform/menus/update-a-menu /api-reference/platform.yaml patch /api/v2/platform/menus/{id} Updates a Menu # Create an Option Type Source: https://spreecommerce.org/docs/api-reference/platform/option-types/create-an-option-type /api-reference/platform.yaml post /api/v2/platform/option_types Creates an Option Type # Delete an Option Type Source: https://spreecommerce.org/docs/api-reference/platform/option-types/delete-an-option-type /api-reference/platform.yaml delete /api/v2/platform/option_types/{id} Deletes an Option Type # Return a list of Option Types Source: https://spreecommerce.org/docs/api-reference/platform/option-types/return-a-list-of-option-types /api-reference/platform.yaml get /api/v2/platform/option_types Returns a list of Option Types # Return an Option Type Source: https://spreecommerce.org/docs/api-reference/platform/option-types/return-an-option-type /api-reference/platform.yaml get /api/v2/platform/option_types/{id} Returns an Option Type # Update an Option Type Source: https://spreecommerce.org/docs/api-reference/platform/option-types/update-an-option-type /api-reference/platform.yaml patch /api/v2/platform/option_types/{id} Updates an Option Type # Create an Option Value Source: https://spreecommerce.org/docs/api-reference/platform/option-values/create-an-option-value /api-reference/platform.yaml post /api/v2/platform/option_values Creates an Option Value # Delete an Option Value Source: https://spreecommerce.org/docs/api-reference/platform/option-values/delete-an-option-value /api-reference/platform.yaml delete /api/v2/platform/option_values/{id} Deletes an Option Value # Return a list of Option Values Source: https://spreecommerce.org/docs/api-reference/platform/option-values/return-a-list-of-option-values /api-reference/platform.yaml get /api/v2/platform/option_values Returns a list of Option Values # Return an Option Value Source: https://spreecommerce.org/docs/api-reference/platform/option-values/return-an-option-value /api-reference/platform.yaml get /api/v2/platform/option_values/{id} Returns an Option Value # Update an Option Value Source: https://spreecommerce.org/docs/api-reference/platform/option-values/update-an-option-value /api-reference/platform.yaml patch /api/v2/platform/option_values/{id} Updates an Option Value # Advances an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/advances-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/advance Advances an Order # Apply Coupon Code for an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/apply-coupon-code-for-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/apply_coupon_code Creates Store Credit payment for an Order # Approves an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/approves-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/approve Approves an Order, when using a token created for a user, it will save this user as the approver # Cancels an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/cancels-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/cancel Cancels an Order, when using a token created for a user, it will save this user as the canceler # Completes an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/completes-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/complete Marks an Order as completed # Creates an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/creates-an-order /api-reference/platform.yaml post /api/v2/platform/orders Creates an Order # Delete an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/delete-an-order /api-reference/platform.yaml delete /api/v2/platform/orders/{id} Deletes an Order # Empties an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/empties-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/empty Removes all line items, promotions, shipment and payments from an Order # Next an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/next-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/next Moves an Order to the next state # Return a list of Orders Source: https://spreecommerce.org/docs/api-reference/platform/orders/return-a-list-of-orders /api-reference/platform.yaml get /api/v2/platform/orders Returns a list of Orders # Return an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/return-an-order /api-reference/platform.yaml get /api/v2/platform/orders/{id} Returns an Order # Update an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/update-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id} Updates an Order # Use Store Credit for an Order Source: https://spreecommerce.org/docs/api-reference/platform/orders/use-store-credit-for-an-order /api-reference/platform.yaml patch /api/v2/platform/orders/{id}/use_store_credit Creates Store Credit payment for an Order # Create a Payment Method Source: https://spreecommerce.org/docs/api-reference/platform/payment-methods/create-a-payment-method /api-reference/platform.yaml post /api/v2/platform/payment_methods Creates a Payment Method # Delete a Payment Method Source: https://spreecommerce.org/docs/api-reference/platform/payment-methods/delete-a-payment-method /api-reference/platform.yaml delete /api/v2/platform/payment_methods/{id} Deletes a Payment Method # Return a list of Payment Methods Source: https://spreecommerce.org/docs/api-reference/platform/payment-methods/return-a-list-of-payment-methods /api-reference/platform.yaml get /api/v2/platform/payment_methods Returns a list of Payment Methods # Return a Payment Method Source: https://spreecommerce.org/docs/api-reference/platform/payment-methods/return-a-payment-method /api-reference/platform.yaml get /api/v2/platform/payment_methods/{id} Returns a Payment Method # Update a Payment Method Source: https://spreecommerce.org/docs/api-reference/platform/payment-methods/update-a-payment-method /api-reference/platform.yaml patch /api/v2/platform/payment_methods/{id} Updates a Payment Method # Delete a Payment Source: https://spreecommerce.org/docs/api-reference/platform/payments/delete-a-payment /api-reference/platform.yaml delete /api/v2/platform/payments/{id} Deletes a Payment # Return a list of Payments Source: https://spreecommerce.org/docs/api-reference/platform/payments/return-a-list-of-payments /api-reference/platform.yaml get /api/v2/platform/payments Returns a list of Payments # Return a Payment Source: https://spreecommerce.org/docs/api-reference/platform/payments/return-a-payment /api-reference/platform.yaml get /api/v2/platform/payments/{id} Returns a Payment # Create a Product Source: https://spreecommerce.org/docs/api-reference/platform/products/create-a-product /api-reference/platform.yaml post /api/v2/platform/products Creates a Product # Delete a Product Source: https://spreecommerce.org/docs/api-reference/platform/products/delete-a-product /api-reference/platform.yaml delete /api/v2/platform/products/{id} Deletes a Product # Return a list of Products Source: https://spreecommerce.org/docs/api-reference/platform/products/return-a-list-of-products /api-reference/platform.yaml get /api/v2/platform/products Returns a list of Products # Return a Product Source: https://spreecommerce.org/docs/api-reference/platform/products/return-a-product /api-reference/platform.yaml get /api/v2/platform/products/{id} Returns a Product # Update a Product Source: https://spreecommerce.org/docs/api-reference/platform/products/update-a-product /api-reference/platform.yaml patch /api/v2/platform/products/{id} Updates a Product # Create a Promotion Action Source: https://spreecommerce.org/docs/api-reference/platform/promotion-actions/create-a-promotion-action /api-reference/platform.yaml post /api/v2/platform/promotion_actions Creates a Promotion Action # Delete a Promotion Action Source: https://spreecommerce.org/docs/api-reference/platform/promotion-actions/delete-a-promotion-action /api-reference/platform.yaml delete /api/v2/platform/promotion_actions/{id} Deletes a Promotion Action # Return a list of Promotion Actions Source: https://spreecommerce.org/docs/api-reference/platform/promotion-actions/return-a-list-of-promotion-actions /api-reference/platform.yaml get /api/v2/platform/promotion_actions Returns a list of Promotion Actions # Return a Promotion Action Source: https://spreecommerce.org/docs/api-reference/platform/promotion-actions/return-a-promotion-action /api-reference/platform.yaml get /api/v2/platform/promotion_actions/{id} Returns a Promotion Action # Update a Promotion Action Source: https://spreecommerce.org/docs/api-reference/platform/promotion-actions/update-a-promotion-action /api-reference/platform.yaml patch /api/v2/platform/promotion_actions/{id} Updates a Promotion Action # Create a Promotion Category Source: https://spreecommerce.org/docs/api-reference/platform/promotion-categories/create-a-promotion-category /api-reference/platform.yaml post /api/v2/platform/promotion_categories Creates a Promotion Category # Delete a Promotion Category Source: https://spreecommerce.org/docs/api-reference/platform/promotion-categories/delete-a-promotion-category /api-reference/platform.yaml delete /api/v2/platform/promotion_categories/{id} Deletes a Promotion Category # Return a list of Promotion Categories Source: https://spreecommerce.org/docs/api-reference/platform/promotion-categories/return-a-list-of-promotion-categories /api-reference/platform.yaml get /api/v2/platform/promotion_categories Returns a list of Promotion Categories # Return a Promotion Category Source: https://spreecommerce.org/docs/api-reference/platform/promotion-categories/return-a-promotion-category /api-reference/platform.yaml get /api/v2/platform/promotion_categories/{id} Returns a Promotion Category # Update a Promotion Category Source: https://spreecommerce.org/docs/api-reference/platform/promotion-categories/update-a-promotion-category /api-reference/platform.yaml patch /api/v2/platform/promotion_categories/{id} Updates a Promotion Category # Create a Promotion Rule Source: https://spreecommerce.org/docs/api-reference/platform/promotion-rules/create-a-promotion-rule /api-reference/platform.yaml post /api/v2/platform/promotion_rules Creates a Promotion Rule # Delete a Promotion Rule Source: https://spreecommerce.org/docs/api-reference/platform/promotion-rules/delete-a-promotion-rule /api-reference/platform.yaml delete /api/v2/platform/promotion_rules/{id} Deletes a Promotion Rule # Return a list of Promotion Rules Source: https://spreecommerce.org/docs/api-reference/platform/promotion-rules/return-a-list-of-promotion-rules /api-reference/platform.yaml get /api/v2/platform/promotion_rules Returns a list of Promotion Rules # Return a Promotion Rule Source: https://spreecommerce.org/docs/api-reference/platform/promotion-rules/return-a-promotion-rule /api-reference/platform.yaml get /api/v2/platform/promotion_rules/{id} Returns a Promotion Rule # Update a Promotion Rule Source: https://spreecommerce.org/docs/api-reference/platform/promotion-rules/update-a-promotion-rule /api-reference/platform.yaml patch /api/v2/platform/promotion_rules/{id} Updates a Promotion Rule # Create a Promotion Source: https://spreecommerce.org/docs/api-reference/platform/promotions/create-a-promotion /api-reference/platform.yaml post /api/v2/platform/promotions Creates a Promotion # Delete a Promotion Source: https://spreecommerce.org/docs/api-reference/platform/promotions/delete-a-promotion /api-reference/platform.yaml delete /api/v2/platform/promotions/{id} Deletes a Promotion # Return a list of Promotions Source: https://spreecommerce.org/docs/api-reference/platform/promotions/return-a-list-of-promotions /api-reference/platform.yaml get /api/v2/platform/promotions Returns a list of Promotions # Return a Promotion Source: https://spreecommerce.org/docs/api-reference/platform/promotions/return-a-promotion /api-reference/platform.yaml get /api/v2/platform/promotions/{id} Returns a Promotion # Update a Promotion Source: https://spreecommerce.org/docs/api-reference/platform/promotions/update-a-promotion /api-reference/platform.yaml patch /api/v2/platform/promotions/{id} Updates a Promotion # Create a Role Source: https://spreecommerce.org/docs/api-reference/platform/roles/create-a-role /api-reference/platform.yaml post /api/v2/platform/roles Creates a Role # Delete a Role Source: https://spreecommerce.org/docs/api-reference/platform/roles/delete-a-role /api-reference/platform.yaml delete /api/v2/platform/roles/{id} Deletes a Role # Return a list of Roles Source: https://spreecommerce.org/docs/api-reference/platform/roles/return-a-list-of-roles /api-reference/platform.yaml get /api/v2/platform/roles Returns a list of Roles # Return a Role Source: https://spreecommerce.org/docs/api-reference/platform/roles/return-a-role /api-reference/platform.yaml get /api/v2/platform/roles/{id} Returns a Role # Update a Role Source: https://spreecommerce.org/docs/api-reference/platform/roles/update-a-role /api-reference/platform.yaml patch /api/v2/platform/roles/{id} Updates a Role # Adds item (Variant) to an existing Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/adds-item-variant-to-an-existing-shipment /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/add_item If selected Variant was already added to Order it will increase the quantity of existing Line Item, if not it will create a new Line Item # Cancels the Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/cancels-the-shipment /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/cancel Cancels the Shipment # Create a Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/create-a-shipment /api-reference/platform.yaml post /api/v2/platform/shipments Creates a Shipment # Delete a Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/delete-a-shipment /api-reference/platform.yaml delete /api/v2/platform/shipments/{id} Deletes a Shipment # Mark Shipment as ready to be shipped Source: https://spreecommerce.org/docs/api-reference/platform/shipments/mark-shipment-as-ready-to-be-shipped /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/ready Marks Shipment as ready to be shipped # Mark Shipment as shipped Source: https://spreecommerce.org/docs/api-reference/platform/shipments/mark-shipment-as-shipped /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/ship Marks Shipment as shipped # Moves Shipment back to pending state Source: https://spreecommerce.org/docs/api-reference/platform/shipments/moves-shipment-back-to-pending-state /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/pend Moves Shipment back to pending state # Removes item (Variant) from Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/removes-item-variant-from-shipment /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/remove_item If selected Variant is removed completely and Shipment doesn't include any other Line Items, Shipment itself will be deleted # Resumes the Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/resumes-the-shipment /api-reference/platform.yaml patch /api/v2/platform/shipments/{id}/resume Resumes previously canceled Shipment # Return a list of Shipments Source: https://spreecommerce.org/docs/api-reference/platform/shipments/return-a-list-of-shipments /api-reference/platform.yaml get /api/v2/platform/shipments Returns a list of Shipments # Return a Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/return-a-shipment /api-reference/platform.yaml get /api/v2/platform/shipments/{id} Returns a Shipment # Update a Shipment Source: https://spreecommerce.org/docs/api-reference/platform/shipments/update-a-shipment /api-reference/platform.yaml patch /api/v2/platform/shipments/{id} Updates a Shipment # Create a Shipping Category Source: https://spreecommerce.org/docs/api-reference/platform/shipping-categories/create-a-shipping-category /api-reference/platform.yaml post /api/v2/platform/shipping_categories Creates a Shipping Category # Delete a Shipping Category Source: https://spreecommerce.org/docs/api-reference/platform/shipping-categories/delete-a-shipping-category /api-reference/platform.yaml delete /api/v2/platform/shipping_categories/{id} Deletes a Shipping Category # Return a list of Shipping Categories Source: https://spreecommerce.org/docs/api-reference/platform/shipping-categories/return-a-list-of-shipping-categories /api-reference/platform.yaml get /api/v2/platform/shipping_categories Returns a list of Shipping Categories # Return a Shipping Category Source: https://spreecommerce.org/docs/api-reference/platform/shipping-categories/return-a-shipping-category /api-reference/platform.yaml get /api/v2/platform/shipping_categories/{id} Returns a Shipping Category # Update a Shipping Category Source: https://spreecommerce.org/docs/api-reference/platform/shipping-categories/update-a-shipping-category /api-reference/platform.yaml patch /api/v2/platform/shipping_categories/{id} Updates a Shipping Category # Create a Shipping Method Source: https://spreecommerce.org/docs/api-reference/platform/shipping-methods/create-a-shipping-method /api-reference/platform.yaml post /api/v2/platform/shipping_methods Creates a Shipping Method # Delete a Shipping Method Source: https://spreecommerce.org/docs/api-reference/platform/shipping-methods/delete-a-shipping-method /api-reference/platform.yaml delete /api/v2/platform/shipping_methods/{id} Deletes a Shipping Method # Return a list of Shipping Methods Source: https://spreecommerce.org/docs/api-reference/platform/shipping-methods/return-a-list-of-shipping-methods /api-reference/platform.yaml get /api/v2/platform/shipping_methods Returns a list of Shipping Methods # Return a Shipping Method Source: https://spreecommerce.org/docs/api-reference/platform/shipping-methods/return-a-shipping-method /api-reference/platform.yaml get /api/v2/platform/shipping_methods/{id} Returns a Shipping Method # Update a Shipping Method Source: https://spreecommerce.org/docs/api-reference/platform/shipping-methods/update-a-shipping-method /api-reference/platform.yaml patch /api/v2/platform/shipping_methods/{id} Updates a Shipping Method # Returns a list of States Source: https://spreecommerce.org/docs/api-reference/platform/states/returns-a-list-of-states /api-reference/platform.yaml get /api/v2/platform/states Returns a list of States # Returns a State Source: https://spreecommerce.org/docs/api-reference/platform/states/returns-a-state /api-reference/platform.yaml get /api/v2/platform/states/{id} Returns a State # Create a Stock Item Source: https://spreecommerce.org/docs/api-reference/platform/stock-items/create-a-stock-item /api-reference/platform.yaml post /api/v2/platform/stock_items Creates a Stock Item # Delete a Stock Item Source: https://spreecommerce.org/docs/api-reference/platform/stock-items/delete-a-stock-item /api-reference/platform.yaml delete /api/v2/platform/stock_items/{id} Deletes a Stock Item # Return a list of Stock Items Source: https://spreecommerce.org/docs/api-reference/platform/stock-items/return-a-list-of-stock-items /api-reference/platform.yaml get /api/v2/platform/stock_items Returns a list of Stock Items # Return a Stock Item Source: https://spreecommerce.org/docs/api-reference/platform/stock-items/return-a-stock-item /api-reference/platform.yaml get /api/v2/platform/stock_items/{id} Returns a Stock Item # Update a Stock Item Source: https://spreecommerce.org/docs/api-reference/platform/stock-items/update-a-stock-item /api-reference/platform.yaml patch /api/v2/platform/stock_items/{id} Updates a Stock Item # Create a Stock Location Source: https://spreecommerce.org/docs/api-reference/platform/stock-locations/create-a-stock-location /api-reference/platform.yaml post /api/v2/platform/stock_locations Creates a Stock Location # Delete a Stock Location Source: https://spreecommerce.org/docs/api-reference/platform/stock-locations/delete-a-stock-location /api-reference/platform.yaml delete /api/v2/platform/stock_locations/{id} Deletes a Stock Location # Return a list of Stock Locations Source: https://spreecommerce.org/docs/api-reference/platform/stock-locations/return-a-list-of-stock-locations /api-reference/platform.yaml get /api/v2/platform/stock_locations Returns a list of Stock Locations # Return a Stock Location Source: https://spreecommerce.org/docs/api-reference/platform/stock-locations/return-a-stock-location /api-reference/platform.yaml get /api/v2/platform/stock_locations/{id} Returns a Stock Location # Update a Stock Location Source: https://spreecommerce.org/docs/api-reference/platform/stock-locations/update-a-stock-location /api-reference/platform.yaml patch /api/v2/platform/stock_locations/{id} Updates a Stock Location # Create a Store Credit Category Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-categories/create-a-store-credit-category /api-reference/platform.yaml post /api/v2/platform/store_credit_categories Creates a Store Credit Category # Delete a Store Credit Category Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-categories/delete-a-store-credit-category /api-reference/platform.yaml delete /api/v2/platform/store_credit_categories/{id} Deletes a Store Credit Category # Return a list of Store Credit Categories Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-categories/return-a-list-of-store-credit-categories /api-reference/platform.yaml get /api/v2/platform/store_credit_categories Returns a list of Store Credit Categories # Return a Store Credit Category Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-categories/return-a-store-credit-category /api-reference/platform.yaml get /api/v2/platform/store_credit_categories/{id} Returns a Store Credit Category # Update a Store Credit Category Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-categories/update-a-store-credit-category /api-reference/platform.yaml patch /api/v2/platform/store_credit_categories/{id} Updates a Store Credit Category # Create a Store Credit Type Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-types/create-a-store-credit-type /api-reference/platform.yaml post /api/v2/platform/store_credit_types Creates a Store Credit Type # Delete a Store Credit Type Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-types/delete-a-store-credit-type /api-reference/platform.yaml delete /api/v2/platform/store_credit_types/{id} Deletes a Store Credit Type # Return a list of Store Credit Types Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-types/return-a-list-of-store-credit-types /api-reference/platform.yaml get /api/v2/platform/store_credit_types Returns a list of Store Credit Types # Return a Store Credit Type Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-types/return-a-store-credit-type /api-reference/platform.yaml get /api/v2/platform/store_credit_types/{id} Returns a Store Credit Type # Update a Store Credit Type Source: https://spreecommerce.org/docs/api-reference/platform/store-credit-types/update-a-store-credit-type /api-reference/platform.yaml patch /api/v2/platform/store_credit_types/{id} Updates a Store Credit Type # Create a Store Credit Source: https://spreecommerce.org/docs/api-reference/platform/store-credits/create-a-store-credit /api-reference/platform.yaml post /api/v2/platform/store_credits Creates a Store Credit # Delete a Store Credit Source: https://spreecommerce.org/docs/api-reference/platform/store-credits/delete-a-store-credit /api-reference/platform.yaml delete /api/v2/platform/store_credits/{id} Deletes a Store Credit # Return a list of Store Credits Source: https://spreecommerce.org/docs/api-reference/platform/store-credits/return-a-list-of-store-credits /api-reference/platform.yaml get /api/v2/platform/store_credits Returns a list of Store Credits # Return a Store Credit Source: https://spreecommerce.org/docs/api-reference/platform/store-credits/return-a-store-credit /api-reference/platform.yaml get /api/v2/platform/store_credits/{id} Returns a Store Credit # Update a Store Credit Source: https://spreecommerce.org/docs/api-reference/platform/store-credits/update-a-store-credit /api-reference/platform.yaml patch /api/v2/platform/store_credits/{id} Updates a Store Credit # Create a Tax Category Source: https://spreecommerce.org/docs/api-reference/platform/tax-categories/create-a-tax-category /api-reference/platform.yaml post /api/v2/platform/tax_categories Creates a Tax Category # Delete a Tax Category Source: https://spreecommerce.org/docs/api-reference/platform/tax-categories/delete-a-tax-category /api-reference/platform.yaml delete /api/v2/platform/tax_categories/{id} Deletes a Tax Category # Return a list of Tax Categories Source: https://spreecommerce.org/docs/api-reference/platform/tax-categories/return-a-list-of-tax-categories /api-reference/platform.yaml get /api/v2/platform/tax_categories Returns a list of Tax Categories # Return a Tax Category Source: https://spreecommerce.org/docs/api-reference/platform/tax-categories/return-a-tax-category /api-reference/platform.yaml get /api/v2/platform/tax_categories/{id} Returns a Tax Category # Update a Tax Category Source: https://spreecommerce.org/docs/api-reference/platform/tax-categories/update-a-tax-category /api-reference/platform.yaml patch /api/v2/platform/tax_categories/{id} Updates a Tax Category # Create a Tax Rate Source: https://spreecommerce.org/docs/api-reference/platform/tax-rates/create-a-tax-rate /api-reference/platform.yaml post /api/v2/platform/tax_rates Creates a Tax Rate # Delete a Tax Rate Source: https://spreecommerce.org/docs/api-reference/platform/tax-rates/delete-a-tax-rate /api-reference/platform.yaml delete /api/v2/platform/tax_rates/{id} Deletes a Tax Rate # Return a list of Tax Rates Source: https://spreecommerce.org/docs/api-reference/platform/tax-rates/return-a-list-of-tax-rates /api-reference/platform.yaml get /api/v2/platform/tax_rates Returns a list of Tax Rates # Return a Tax Rate Source: https://spreecommerce.org/docs/api-reference/platform/tax-rates/return-a-tax-rate /api-reference/platform.yaml get /api/v2/platform/tax_rates/{id} Returns a Tax Rate # Update a Tax Rate Source: https://spreecommerce.org/docs/api-reference/platform/tax-rates/update-a-tax-rate /api-reference/platform.yaml patch /api/v2/platform/tax_rates/{id} Updates a Tax Rate # Create a Taxonomy Source: https://spreecommerce.org/docs/api-reference/platform/taxonomies/create-a-taxonomy /api-reference/platform.yaml post /api/v2/platform/taxonomies Creates a Taxonomy # Delete a Taxonomy Source: https://spreecommerce.org/docs/api-reference/platform/taxonomies/delete-a-taxonomy /api-reference/platform.yaml delete /api/v2/platform/taxonomies/{id} Deletes a Taxonomy # Return a list of Taxonomies Source: https://spreecommerce.org/docs/api-reference/platform/taxonomies/return-a-list-of-taxonomies /api-reference/platform.yaml get /api/v2/platform/taxonomies Returns a list of Taxonomies # Return a Taxonomy Source: https://spreecommerce.org/docs/api-reference/platform/taxonomies/return-a-taxonomy /api-reference/platform.yaml get /api/v2/platform/taxonomies/{id} Returns a Taxonomy # Update a Taxonomy Source: https://spreecommerce.org/docs/api-reference/platform/taxonomies/update-a-taxonomy /api-reference/platform.yaml patch /api/v2/platform/taxonomies/{id} Updates a Taxonomy # Create a Taxon Source: https://spreecommerce.org/docs/api-reference/platform/taxons/create-a-taxon /api-reference/platform.yaml post /api/v2/platform/taxons Creates a Taxon # Delete a Taxon Source: https://spreecommerce.org/docs/api-reference/platform/taxons/delete-a-taxon /api-reference/platform.yaml delete /api/v2/platform/taxons/{id} Deletes a Taxon # Reposition a Taxon Source: https://spreecommerce.org/docs/api-reference/platform/taxons/reposition-a-taxon /api-reference/platform.yaml patch /api/v2/platform/taxons/{id}/reposition Reposition a Taxon # Return a list of Taxons Source: https://spreecommerce.org/docs/api-reference/platform/taxons/return-a-list-of-taxons /api-reference/platform.yaml get /api/v2/platform/taxons Returns a list of Taxons # Return a Taxon Source: https://spreecommerce.org/docs/api-reference/platform/taxons/return-a-taxon /api-reference/platform.yaml get /api/v2/platform/taxons/{id} Returns a Taxon # Update a Taxon Source: https://spreecommerce.org/docs/api-reference/platform/taxons/update-a-taxon /api-reference/platform.yaml patch /api/v2/platform/taxons/{id} Updates a Taxon # Create a User Source: https://spreecommerce.org/docs/api-reference/platform/users/create-a-user /api-reference/platform.yaml post /api/v2/platform/users Creates a User # Delete a User Source: https://spreecommerce.org/docs/api-reference/platform/users/delete-a-user /api-reference/platform.yaml delete /api/v2/platform/users/{id} Deletes a User # Return a list of Users Source: https://spreecommerce.org/docs/api-reference/platform/users/return-a-list-of-users /api-reference/platform.yaml get /api/v2/platform/users Returns a list of Users # Return a User Source: https://spreecommerce.org/docs/api-reference/platform/users/return-a-user /api-reference/platform.yaml get /api/v2/platform/users/{id} Returns a User # Update a User Source: https://spreecommerce.org/docs/api-reference/platform/users/update-a-user /api-reference/platform.yaml patch /api/v2/platform/users/{id} Updates a User # Delete a Variant Source: https://spreecommerce.org/docs/api-reference/platform/variants/delete-a-variant /api-reference/platform.yaml delete /api/v2/platform/variants/{id} Deletes a Variant # Return a list of Variants Source: https://spreecommerce.org/docs/api-reference/platform/variants/return-a-list-of-variants /api-reference/platform.yaml get /api/v2/platform/variants Returns a list of Variants # Return a Variant Source: https://spreecommerce.org/docs/api-reference/platform/variants/return-a-variant /api-reference/platform.yaml get /api/v2/platform/variants/{id} Returns a Variant # Approves Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/approves-vendor /api-reference/platform.yaml patch /api/v2/platform/vendors/{id}/approve Marks Vendor as approved, and triggers an approval email send out to the vendor's contact person. Also activated Vendor products will become available for purchase. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Completes onboarding process Source: https://spreecommerce.org/docs/api-reference/platform/vendors/completes-onboarding-process /api-reference/platform.yaml patch /api/v2/platform/vendors/{id}/complete_onboarding Marks Vendor as onboarding complete. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Create a Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/create-a-vendor /api-reference/platform.yaml post /api/v2/platform/vendors Creates a Vendor. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Delete a Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/delete-a-vendor /api-reference/platform.yaml delete /api/v2/platform/vendors/{id} Deletes a Vendor. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Invites Vendor to the platform Source: https://spreecommerce.org/docs/api-reference/platform/vendors/invites-vendor-to-the-platform /api-reference/platform.yaml patch /api/v2/platform/vendors/{id}/invite Also triggers an invitation email send out to the vendor's contact person. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Rejects Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/rejects-vendor /api-reference/platform.yaml patch /api/v2/platform/vendors/{id}/reject Marks Vendor as rejected, and triggers an approval email send out to the vendor's contact person. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Return a list of Vendors Source: https://spreecommerce.org/docs/api-reference/platform/vendors/return-a-list-of-vendors /api-reference/platform.yaml get /api/v2/platform/vendors Returns a list of Vendors. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Return a Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/return-a-vendor /api-reference/platform.yaml get /api/v2/platform/vendors/{id} Returns a Vendor. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Start onboarding process Source: https://spreecommerce.org/docs/api-reference/platform/vendors/start-onboarding-process /api-reference/platform.yaml patch /api/v2/platform/vendors/{id}/start_onboarding Marks Vendor as onboarding in progress. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Suspends Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/suspends-vendor /api-reference/platform.yaml patch /api/v2/platform/vendors/{id}/suspend Marks Vendor as suspended, and triggers an approval email send out to the vendor's contact person. Also the vendor's products will become unavailable for purchase and will be hidden from the storefront. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Update a Vendor Source: https://spreecommerce.org/docs/api-reference/platform/vendors/update-a-vendor /api-reference/platform.yaml patch /api/v2/platform/vendors/{id} Updates a Vendor. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Return a list of Webhook Events Source: https://spreecommerce.org/docs/api-reference/platform/webhook-events/return-a-list-of-webhook-events /api-reference/platform.yaml get /api/v2/platform/webhooks/events Returns a list of Webhook Events # Create a Webhook Subscriber Source: https://spreecommerce.org/docs/api-reference/platform/webhook-subscribers/create-a-webhook-subscriber /api-reference/platform.yaml post /api/v2/platform/webhooks/subscribers Creates a Webhook Subscriber # Delete a Webhook Subscriber Source: https://spreecommerce.org/docs/api-reference/platform/webhook-subscribers/delete-a-webhook-subscriber /api-reference/platform.yaml delete /api/v2/platform/webhooks/subscribers/{id} Deletes a Webhook Subscriber # Return a list of Webhook Subscribers Source: https://spreecommerce.org/docs/api-reference/platform/webhook-subscribers/return-a-list-of-webhook-subscribers /api-reference/platform.yaml get /api/v2/platform/webhooks/subscribers Returns a list of Webhook Subscribers # Return a Webhook Subscriber Source: https://spreecommerce.org/docs/api-reference/platform/webhook-subscribers/return-a-webhook-subscriber /api-reference/platform.yaml get /api/v2/platform/webhooks/subscribers/{id} Returns a Webhook Subscriber # Update a Webhook Subscriber Source: https://spreecommerce.org/docs/api-reference/platform/webhook-subscribers/update-a-webhook-subscriber /api-reference/platform.yaml patch /api/v2/platform/webhooks/subscribers/{id} Updates a Webhook Subscriber # Create a Wished Item Source: https://spreecommerce.org/docs/api-reference/platform/wished-items/create-a-wished-item /api-reference/platform.yaml post /api/v2/platform/wished_items Creates a Wished Item # Delete a Wished Item Source: https://spreecommerce.org/docs/api-reference/platform/wished-items/delete-a-wished-item /api-reference/platform.yaml delete /api/v2/platform/wished_items/{id} Deletes a Wished Item # Return a list of Wished Items Source: https://spreecommerce.org/docs/api-reference/platform/wished-items/return-a-list-of-wished-items /api-reference/platform.yaml get /api/v2/platform/wished_items Returns a list of Wished Items # Return a Wished Item Source: https://spreecommerce.org/docs/api-reference/platform/wished-items/return-a-wished-item /api-reference/platform.yaml get /api/v2/platform/wished_items/{id} Returns a Wished Item # Update a Wished Item Source: https://spreecommerce.org/docs/api-reference/platform/wished-items/update-a-wished-item /api-reference/platform.yaml patch /api/v2/platform/wished_items/{id} Updates a Wished Item # Create a Wishlist Source: https://spreecommerce.org/docs/api-reference/platform/wishlists/create-a-wishlist /api-reference/platform.yaml post /api/v2/platform/wishlists Creates a Wishlist # Delete a Wishlist Source: https://spreecommerce.org/docs/api-reference/platform/wishlists/delete-a-wishlist /api-reference/platform.yaml delete /api/v2/platform/wishlists/{id} Deletes a Wishlist # Return a list of Wishlists Source: https://spreecommerce.org/docs/api-reference/platform/wishlists/return-a-list-of-wishlists /api-reference/platform.yaml get /api/v2/platform/wishlists Returns a list of Wishlists # Return a Wishlist Source: https://spreecommerce.org/docs/api-reference/platform/wishlists/return-a-wishlist /api-reference/platform.yaml get /api/v2/platform/wishlists/{id} Returns a Wishlist # Update a Wishlist Source: https://spreecommerce.org/docs/api-reference/platform/wishlists/update-a-wishlist /api-reference/platform.yaml patch /api/v2/platform/wishlists/{id} Updates a Wishlist # Create a Zone Source: https://spreecommerce.org/docs/api-reference/platform/zones/create-a-zone /api-reference/platform.yaml post /api/v2/platform/zones Creates a Zone # Delete a Zone Source: https://spreecommerce.org/docs/api-reference/platform/zones/delete-a-zone /api-reference/platform.yaml delete /api/v2/platform/zones/{id} Deletes a Zone # Return a list of Zones Source: https://spreecommerce.org/docs/api-reference/platform/zones/return-a-list-of-zones /api-reference/platform.yaml get /api/v2/platform/zones Returns a list of Zones # Return a Zone Source: https://spreecommerce.org/docs/api-reference/platform/zones/return-a-zone /api-reference/platform.yaml get /api/v2/platform/zones/{id} Returns a Zone # Update a Zone Source: https://spreecommerce.org/docs/api-reference/platform/zones/update-a-zone /api-reference/platform.yaml patch /api/v2/platform/zones/{id} Updates a Zone # Authentication Source: https://spreecommerce.org/docs/api-reference/store-api/authentication How to authenticate requests to the Store API 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. ## API Key (Required) 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. Pass the key via the `X-Spree-Api-Key` header: ```typescript SDK theme={"theme":"night-owl"} import { createClient } from '@spree/sdk' const client = createClient({ baseUrl: 'http://localhost:3000', publishableKey: 'pk_xxx', }) // The SDK automatically sends the publishable key with every request const products = await client.products.list() ``` ```bash cURL theme={"theme":"night-owl"} curl -X GET 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' ``` Publishable API keys are prefixed with `pk_`. You can create them in the Spree Admin under **Settings > API Keys** or via the Spree CLI: ```bash theme={"theme":"night-owl"} spree api-key create # Create a new API key spree api-key list # List existing API keys ``` If you omit the API key, the API returns a `401 Unauthorized` error: ```json theme={"theme":"night-owl"} { "error": { "code": "invalid_token", "message": "Valid API key required" } } ``` ## JWT Token (Authenticated Customer) 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. ### Login ```typescript SDK theme={"theme":"night-owl"} // Login to get a JWT token const { token, user } = await client.auth.login({ email: 'customer@example.com', password: 'password123', }) // Use the token for authenticated requests const orders = await client.customer.orders.list({}, { token }) ``` ```bash cURL theme={"theme":"night-owl"} # Login curl -X POST 'http://localhost:3000/api/v3/store/auth/login' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Content-Type: application/json' \ -d '{"email": "customer@example.com", "password": "password123"}' # Use the returned token for authenticated requests curl -X GET 'http://localhost:3000/api/v3/store/orders' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Authorization: Bearer ' ``` ### Register ```typescript SDK theme={"theme":"night-owl"} const { token, user } = await client.customers.create({ email: 'new@example.com', password: 'password123', password_confirmation: 'password123', first_name: 'John', last_name: 'Doe', }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X POST 'http://localhost:3000/api/v3/store/customers' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Content-Type: application/json' \ -d '{ "email": "new@example.com", "password": "password123", "password_confirmation": "password123", "first_name": "John", "last_name": "Doe" }' ``` ### Token Refresh JWT tokens expire after 1 hour by default. Use the refresh endpoint to get a new token: ```typescript SDK theme={"theme":"night-owl"} const { token } = await client.auth.refresh({ token: currentToken }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X POST 'http://localhost:3000/api/v3/store/auth/refresh' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Authorization: Bearer ' ``` ## Order Token (Guest Checkout) For guest checkout flows, use the **order token** returned when creating a cart. This allows unauthenticated users to manage their cart and complete checkout. Pass the token via the `x-spree-token` header: ```typescript SDK theme={"theme":"night-owl"} // Create a cart (guest) const cart = await client.carts.create() // Use spreeToken for all guest cart operations const options = { spreeToken: cart.token } // Add items await client.carts.items.create(cart.id, { variant_id: 'variant_abc123', quantity: 1, }, options) ``` ```bash cURL theme={"theme":"night-owl"} # Create a cart curl -X POST 'http://localhost:3000/api/v3/store/carts' \ -H 'X-Spree-Api-Key: pk_xxx' # Use the returned token for cart operations curl -X POST 'http://localhost:3000/api/v3/store/carts/cart_xxx/items' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Token: ' \ -H 'Content-Type: application/json' \ -d '{"variant_id": "variant_abc123", "quantity": 1}' ``` ### Associating a Guest Cart After a guest user logs in, you can associate their guest cart with their account: ```typescript SDK theme={"theme":"night-owl"} await client.carts.associate(cart.id, { token: jwtToken, spreeToken: cart.token, }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X PATCH 'http://localhost:3000/api/v3/store/carts/cart_xxx/associate' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Authorization: Bearer ' \ -H 'X-Spree-Token: ' ``` ## Authentication Summary | Method | Header | Use Case | | ----------- | ------------------------------- | ------------------------------ | | API Key | `X-Spree-Api-Key: pk_xxx` | All requests (required) | | JWT Token | `Authorization: Bearer ` | Authenticated customer actions | | Order Token | `X-Spree-Token: ` | Guest cart and checkout | # Login Source: https://spreecommerce.org/docs/api-reference/store-api/authentication/login /api-reference/store.yaml post /api/v3/store/auth/login Authenticates a customer and returns a JWT access token + refresh token. Dispatches by the `provider` field to a strategy registered in `Spree.store_authentication_strategies`. When `provider` is omitted it defaults to `email`, which uses the built-in email/password strategy. To plug in a third-party identity provider (Auth0, Okta, Firebase, a custom JWT issuer, SAML, etc.), register a `Spree::Authentication::Strategies::BaseStrategy` subclass under a provider key, then send `{ "provider": "", ... }` with the fields your strategy requires. The endpoint returns the same Spree-issued JWT + refresh token regardless of which strategy authenticated the request. # Logout Source: https://spreecommerce.org/docs/api-reference/store-api/authentication/logout /api-reference/store.yaml post /api/v3/store/auth/logout Revokes the submitted refresh token. The refresh token itself is the credential — no Authorization header is required, so a client with an expired access JWT can still log out. # Refresh token Source: https://spreecommerce.org/docs/api-reference/store-api/authentication/refresh-token /api-reference/store.yaml post /api/v3/store/auth/refresh Exchanges a refresh token for a new access JWT and rotated refresh token. No Authorization header needed. # Request a password reset Source: https://spreecommerce.org/docs/api-reference/store-api/authentication/request-a-password-reset /api-reference/store.yaml post /api/v3/store/password_resets Sends a password reset email if an account exists for the given email address. Always returns 202 Accepted to prevent email enumeration. # Reset password with token Source: https://spreecommerce.org/docs/api-reference/store-api/authentication/reset-password-with-token /api-reference/store.yaml patch /api/v3/store/password_resets/{token} Resets the password using a token received via email. Returns a JWT token on success (auto-login). # Add item to cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/add-item-to-cart /api-reference/store.yaml post /api/v3/store/carts/{cart_id}/items Adds a variant to the cart. Creates a new line item or increases quantity if variant already in cart. # Apply discount code Source: https://spreecommerce.org/docs/api-reference/store-api/carts/apply-discount-code /api-reference/store.yaml post /api/v3/store/carts/{cart_id}/discount_codes Applies a promotion discount code to the cart. The code is matched case-insensitively. For gift cards, use the dedicated `POST /carts/{cart_id}/gift_cards` endpoint instead. # Apply gift card Source: https://spreecommerce.org/docs/api-reference/store-api/carts/apply-gift-card /api-reference/store.yaml post /api/v3/store/carts/{cart_id}/gift_cards Applies a gift card to the cart. Gift cards are treated as a payment method, not a discount — the cart `total` remains unchanged while `amount_due` is reduced. For promotion discount codes, use the `POST /carts/{cart_id}/discount_codes` endpoint instead. # Apply store credit Source: https://spreecommerce.org/docs/api-reference/store-api/carts/apply-store-credit /api-reference/store.yaml post /api/v3/store/carts/{cart_id}/store_credits Applies store credit to the cart during checkout. # Associate guest cart with authenticated user Source: https://spreecommerce.org/docs/api-reference/store-api/carts/associate-guest-cart-with-authenticated-user /api-reference/store.yaml patch /api/v3/store/carts/{id}/associate Associates a guest cart with the currently authenticated user. Requires JWT authentication. The cart must not belong to another user. # Complete cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/complete-cart /api-reference/store.yaml post /api/v3/store/carts/{id}/complete Completes the cart and finalizes the purchase. Returns an Order (not Cart). # Complete payment session Source: https://spreecommerce.org/docs/api-reference/store-api/carts/complete-payment-session /api-reference/store.yaml patch /api/v3/store/carts/{cart_id}/payment_sessions/{id}/complete Completes a payment session by confirming the payment with the provider. This triggers payment capture/authorization and order completion. # Create a new cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/create-a-new-cart /api-reference/store.yaml post /api/v3/store/carts Creates a new shopping cart. Can be created by guests or authenticated customers. Returns a `token` that must be used for guest access to the cart. # Create payment Source: https://spreecommerce.org/docs/api-reference/store-api/carts/create-payment /api-reference/store.yaml post /api/v3/store/carts/{cart_id}/payments Creates a payment for a non-session payment method (e.g. Check, Cash on Delivery, Bank Transfer). For payment methods that require a session (e.g. Stripe, PayPal), use the payment sessions endpoint instead. # Create payment session Source: https://spreecommerce.org/docs/api-reference/store-api/carts/create-payment-session /api-reference/store.yaml post /api/v3/store/carts/{cart_id}/payment_sessions Creates a new payment session for the cart. Delegates to the payment gateway to initialize a provider-specific session (e.g. Stripe PaymentIntent, Adyen session, PayPal order). # Delete a cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/delete-a-cart /api-reference/store.yaml delete /api/v3/store/carts/{id} Deletes/abandons the cart. # Get a cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/get-a-cart /api-reference/store.yaml get /api/v3/store/carts/{id} Returns a shopping cart by prefixed ID. Authorize via x-spree-token header (guest) or JWT Bearer token (authenticated user). # Get payment session Source: https://spreecommerce.org/docs/api-reference/store-api/carts/get-payment-session /api-reference/store.yaml get /api/v3/store/carts/{cart_id}/payment_sessions/{id} Returns a single payment session with its current status and provider data. # List active carts Source: https://spreecommerce.org/docs/api-reference/store-api/carts/list-active-carts /api-reference/store.yaml get /api/v3/store/carts Returns all active (incomplete) carts for the authenticated user. # Remove discount code Source: https://spreecommerce.org/docs/api-reference/store-api/carts/remove-discount-code /api-reference/store.yaml delete /api/v3/store/carts/{cart_id}/discount_codes/{id} Removes a previously applied discount code from the cart. The ID is the discount code string itself. # Remove gift card Source: https://spreecommerce.org/docs/api-reference/store-api/carts/remove-gift-card /api-reference/store.yaml delete /api/v3/store/carts/{cart_id}/gift_cards/{id} Removes a previously applied gift card from the cart. # Remove line item from cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/remove-line-item-from-cart /api-reference/store.yaml delete /api/v3/store/carts/{cart_id}/items/{id} Removes a line item from the cart # Remove store credit Source: https://spreecommerce.org/docs/api-reference/store-api/carts/remove-store-credit /api-reference/store.yaml delete /api/v3/store/carts/{cart_id}/store_credits Removes store credit from the cart. # Select delivery rate for fulfillment Source: https://spreecommerce.org/docs/api-reference/store-api/carts/select-delivery-rate-for-fulfillment /api-reference/store.yaml patch /api/v3/store/carts/{cart_id}/fulfillments/{id} Selects a delivery rate for a specific fulfillment and auto-advances checkout. # Update a cart Source: https://spreecommerce.org/docs/api-reference/store-api/carts/update-a-cart /api-reference/store.yaml patch /api/v3/store/carts/{id} Updates cart info (email, addresses, customer note). When addresses change, the order state is reverted to address to ensure shipments are recalculated. # Update line item quantity Source: https://spreecommerce.org/docs/api-reference/store-api/carts/update-line-item-quantity /api-reference/store.yaml patch /api/v3/store/carts/{cart_id}/items/{id} Updates the quantity of a line item in the cart # Update payment session Source: https://spreecommerce.org/docs/api-reference/store-api/carts/update-payment-session /api-reference/store.yaml patch /api/v3/store/carts/{cart_id}/payment_sessions/{id} Updates a payment session. Delegates to the payment gateway to sync changes with the provider. # Complete payment setup session Source: https://spreecommerce.org/docs/api-reference/store-api/customers/complete-payment-setup-session /api-reference/store.yaml patch /api/v3/store/customers/me/payment_setup_sessions/{id}/complete Completes a payment setup session by confirming the setup with the provider, resulting in a saved payment method. # Create an address Source: https://spreecommerce.org/docs/api-reference/store-api/customers/create-an-address /api-reference/store.yaml post /api/v3/store/customers/me/addresses Adds a new address to the customer address book # Create payment setup session Source: https://spreecommerce.org/docs/api-reference/store-api/customers/create-payment-setup-session /api-reference/store.yaml post /api/v3/store/customers/me/payment_setup_sessions Creates a new payment setup session for saving a payment method for future use. Delegates to the payment gateway to initialize a provider-specific setup flow (e.g. Stripe SetupIntent, Adyen zero-auth tokenization). # Delete a credit card Source: https://spreecommerce.org/docs/api-reference/store-api/customers/delete-a-credit-card /api-reference/store.yaml delete /api/v3/store/customers/me/credit_cards/{id} Removes a saved credit card from the customer account # Delete an address Source: https://spreecommerce.org/docs/api-reference/store-api/customers/delete-an-address /api-reference/store.yaml delete /api/v3/store/customers/me/addresses/{id} # Get a credit card Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-a-credit-card /api-reference/store.yaml get /api/v3/store/customers/me/credit_cards/{id} Returns a saved credit card by its ID # Get a gift card Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-a-gift-card /api-reference/store.yaml get /api/v3/store/customers/me/gift_cards/{id} Returns a gift card by its ID # Get a store credit Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-a-store-credit /api-reference/store.yaml get /api/v3/store/customers/me/store_credits/{id} # Get an address Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-an-address /api-reference/store.yaml get /api/v3/store/customers/me/addresses/{id} # Get an order Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-an-order /api-reference/store.yaml get /api/v3/store/customers/me/orders/{id} Returns a single completed order for the authenticated customer. # Get current customer profile Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-current-customer-profile /api-reference/store.yaml get /api/v3/store/customers/me Returns the profile of the currently authenticated customer # Get payment setup session Source: https://spreecommerce.org/docs/api-reference/store-api/customers/get-payment-setup-session /api-reference/store.yaml get /api/v3/store/customers/me/payment_setup_sessions/{id} Returns a payment setup session with its current status and provider data. # List customer addresses Source: https://spreecommerce.org/docs/api-reference/store-api/customers/list-customer-addresses /api-reference/store.yaml get /api/v3/store/customers/me/addresses Returns all addresses in the customer address book # List gift cards Source: https://spreecommerce.org/docs/api-reference/store-api/customers/list-gift-cards /api-reference/store.yaml get /api/v3/store/customers/me/gift_cards Returns all gift cards for the authenticated customer # List orders Source: https://spreecommerce.org/docs/api-reference/store-api/customers/list-orders /api-reference/store.yaml get /api/v3/store/customers/me/orders Returns a paginated list of completed orders for the authenticated customer. # List saved credit cards Source: https://spreecommerce.org/docs/api-reference/store-api/customers/list-saved-credit-cards /api-reference/store.yaml get /api/v3/store/customers/me/credit_cards Returns all saved credit cards for the authenticated customer # List store credits Source: https://spreecommerce.org/docs/api-reference/store-api/customers/list-store-credits /api-reference/store.yaml get /api/v3/store/customers/me/store_credits Returns store credits for the authenticated customer, filtered by current store and currency. Supports Ransack filtering. # Register a new customer Source: https://spreecommerce.org/docs/api-reference/store-api/customers/register-a-new-customer /api-reference/store.yaml post /api/v3/store/customers Creates a new customer account and returns a JWT token # Update an address Source: https://spreecommerce.org/docs/api-reference/store-api/customers/update-an-address /api-reference/store.yaml patch /api/v3/store/customers/me/addresses/{id} # Update current customer profile Source: https://spreecommerce.org/docs/api-reference/store-api/customers/update-current-customer-profile /api-reference/store.yaml patch /api/v3/store/customers/me Updates the profile of the currently authenticated customer # Download a digital product Source: https://spreecommerce.org/docs/api-reference/store-api/digitals/download-a-digital-product /api-reference/store.yaml get /api/v3/store/digitals/{token} Downloads a digital product file using the digital link token. The token is provided via the `download_url` field on digital links returned with order line items. No API key or authentication required — the token itself grants access. Each download increments the access counter. Downloads may be limited by store settings (number of downloads and/or time-based expiration). # Errors Source: https://spreecommerce.org/docs/api-reference/store-api/errors Reference for Spree Store API error responses — JSON format, machine-readable codes, HTTP status codes, validation details, and retry strategies. 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`. ## Error Response Format All errors return a JSON object with a single `error` key: ```json theme={"theme":"night-owl"} { "error": { "code": "record_not_found", "message": "Product not found" } } ``` Validation errors include an additional `details` field with per-field error messages: ```json theme={"theme":"night-owl"} { "error": { "code": "validation_error", "message": "Name can't be blank and Email is invalid", "details": { "name": ["can't be blank"], "email": ["is invalid"] } } } ``` ### Schema | Field | Type | Description | | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `error.code` | `string` | Machine-readable error code (see table below) | | `error.message` | `string` | Human-readable description of the error | | `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. | ## HTTP Status Codes | Status | Meaning | When | | ------ | --------------------- | ---------------------------------------------------------------------------------- | | `400` | Bad Request | Missing required parameters, malformed JSON, invalid arguments | | `401` | Unauthorized | Missing or invalid API key, expired JWT token | | `403` | Forbidden | Authenticated but not authorized for this resource | | `404` | Not Found | Resource doesn't exist or isn't accessible | | `409` | Conflict | Resource was modified by another request (concurrent update) | | `422` | Unprocessable Content | Validation failed, invalid state transition, payment error | | `429` | Too Many Requests | Rate limit exceeded (see [Rate Limiting](/docs/api-reference/store-api/rate-limitting)) | ## Error Codes ### Authentication & Authorization | Code | Status | Description | | ------------------------- | ------ | -------------------------------------------- | | `authentication_required` | 401 | Request requires a valid JWT token | | `authentication_failed` | 401 | Email or password is incorrect | | `invalid_token` | 401 | API key or JWT token is invalid or expired | | `invalid_provider` | 400 | OAuth provider not recognized | | `access_denied` | 403 | User doesn't have permission for this action | ### Resource Errors | Code | Status | Description | | ------------------ | ------ | --------------------------------------------------------------- | | `record_not_found` | 404 | Resource doesn't exist or isn't accessible in the current store | | `resource_invalid` | 422 | Resource couldn't be saved | ### Validation Errors | Code | Status | Description | | ------------------- | ------ | --------------------------------------------------------------------- | | `validation_error` | 422 | Model validation failed. Check `details` for field-specific messages. | | `parameter_missing` | 400 | A required parameter is missing | | `parameter_invalid` | 400 | A parameter has an invalid value | ### Cart Errors | Code | Status | Description | | ------------------------ | ------ | ----------------------------------------------------------------------------------- | | `cart_not_found` | 404 | Cart doesn't exist or doesn't belong to the current user/guest | | `cart_cannot_complete` | 422 | Cart cannot be completed (e.g., missing payment or address) | | `cart_cannot_transition` | 422 | Internal state machine transition failed (e.g., completing a cart that isn't ready) | | `cart_empty` | 422 | Cart has no line items | | `cart_invalid_state` | 422 | Cart is in an invalid state for this operation | | `cart_already_updated` | 409 | Cart was modified by another request (optimistic locking conflict) | ### Order Errors | Code | Status | Description | | ----------------- | ------ | ------------------------------------------------------------------------- | | `order_not_found` | 404 | Completed order doesn't exist or doesn't belong to the current user/guest | ### Line Item & Stock Errors | Code | Status | Description | | --------------------- | ------ | -------------------------------------------------- | | `line_item_not_found` | 404 | Line item doesn't exist in this order | | `variant_not_found` | 404 | Variant doesn't exist or isn't available | | `insufficient_stock` | 422 | Not enough stock to fulfill the requested quantity | | `invalid_quantity` | 422 | Quantity must be a positive integer | ### Payment Errors | Code | Status | Description | | -------------------------- | ------ | --------------------------------------------- | | `payment_failed` | 422 | Payment was declined or couldn't be processed | | `payment_processing_error` | 422 | Error communicating with the payment gateway | | `gateway_error` | 422 | Payment gateway returned an error | ### Digital Download Errors | Code | Status | Description | | ------------------------- | ------ | -------------------------------------------------- | | `attachment_missing` | 403 | File attachment not found for this digital product | | `download_unauthorized` | 403 | User is not authorized to download this file | | `digital_link_expired` | 403 | Download link has expired | | `download_limit_exceeded` | 403 | Maximum number of downloads reached | ### Concurrency & Idempotency Errors | Code | Status | Description | | ------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------- | | `order_already_updated` | 409 | Order was modified by another concurrent request. Retry with fresh data. | | `idempotency_key_reused` | 422 | Idempotency key was already used with different request parameters. See [Idempotency](/docs/api-reference/store-api/idempotency). | ### Other Errors | Code | Status | Description | | --------------------- | ------ | -------------------------------------------------------------- | | `rate_limit_exceeded` | 429 | Too many requests. Retry after the `Retry-After` header value. | | `request_too_large` | 413 | Request body exceeds the size limit | | `processing_error` | 422 | Generic server-side processing error | | `invalid_request` | 400 | Request is malformed (e.g., invalid JSON body) | ## Examples ### Not Found ```bash theme={"theme":"night-owl"} curl https://api.mystore.com/api/v3/store/products/prod_nonexistent \ -H "X-Spree-API-Key: pk_xxx" ``` ```json 404 theme={"theme":"night-owl"} { "error": { "code": "record_not_found", "message": "Product not found" } } ``` ### Validation Error ```bash theme={"theme":"night-owl"} curl -X POST https://api.mystore.com/api/v3/store/account/addresses \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{}' ``` ```json 422 theme={"theme":"night-owl"} { "error": { "code": "validation_error", "message": "First name can't be blank, Address can't be blank, City can't be blank, and Country can't be blank", "details": { "firstname": ["can't be blank"], "address1": ["can't be blank"], "city": ["can't be blank"], "country": ["can't be blank"] } } } ``` ### Insufficient Stock ```bash theme={"theme":"night-owl"} curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/items \ -H "X-Spree-API-Key: pk_xxx" \ -H "X-Spree-Token: abc123" \ -H "Content-Type: application/json" \ -d '{"variant_id": "var_xxx", "quantity": 999}' ``` ```json 422 theme={"theme":"night-owl"} { "error": { "code": "insufficient_stock", "message": "Quantity selected exceeds available stock" } } ``` ### Invalid State Transition ```bash theme={"theme":"night-owl"} curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/complete \ -H "X-Spree-API-Key: pk_xxx" ``` ```json 422 theme={"theme":"night-owl"} { "error": { "code": "cart_cannot_transition", "message": "Cannot transition cart to complete — ensure address, shipping, and payment are set" } } ``` ## Handling Errors in the SDK The `@spree/sdk` package throws a `SpreeError` for all non-2xx responses: ```typescript theme={"theme":"night-owl"} import { SpreeError } from '@spree/sdk'; try { const product = await client.products.get('nonexistent'); } 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); // undefined (or field errors for validation) } } ``` ### Common Patterns Handle specific error codes: ```typescript theme={"theme":"night-owl"} try { await client.carts.items.create(cartId, { variant_id: variantId, quantity: 1, }); } catch (error) { if (error instanceof SpreeError) { switch (error.code) { case 'insufficient_stock': showNotification('This item is out of stock'); break; case 'variant_not_found': showNotification('This product is no longer available'); break; default: showNotification(error.message); } } } ``` Display validation errors per field: ```typescript theme={"theme":"night-owl"} try { await client.customer.addresses.create(addressData); } catch (error) { if (error instanceof SpreeError && error.details) { // error.details = { city: ["can't be blank"], zipcode: ["is invalid"] } for (const [field, messages] of Object.entries(error.details)) { setFieldError(field, messages.join(', ')); } } } ``` # Idempotency Source: https://spreecommerce.org/docs/api-reference/store-api/idempotency Use Idempotency-Key headers on Spree Store API mutations to safely retry carts, checkouts, and payments without creating duplicate side effects. 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. ## How It Works Include an `Idempotency-Key` header with a unique value (e.g., a UUID) on any supported request. The API will: 1. **First request** — process normally and cache the response for 24 hours 2. **Duplicate request** (same key, same parameters) — return the cached response without re-executing the operation 3. **Key reuse with different parameters** — return a `422` error to prevent misuse ```bash theme={"theme":"night-owl"} curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/items \ -H "X-Spree-API-Key: pk_xxx" \ -H "X-Spree-Token: abc123" \ -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \ -H "Content-Type: application/json" \ -d '{"variant_id": "variant_xxx", "quantity": 1}' ``` 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. ## Supported Endpoints | Endpoint | Actions | | ------------------------------------------------ | ------------------------ | | `POST /carts` | Cart creation | | `PATCH /carts/:id` | Cart updates | | `POST /carts/:id/complete` | Order completion | | `POST /carts/:id/items` | Adding items to cart | | `POST /carts/:id/payment_sessions` | Payment session creation | | `PATCH /carts/:id/payment_sessions/:id/complete` | Payment completion | | `POST /carts/:id/coupon_codes` | Applying coupon codes | | `POST /carts/:id/store_credits` | Applying store credits | Idempotency keys are ignored on `GET`, `DELETE`, and other non-supported actions. ## Key Requirements * Must be a string of **255 characters or less** * Should be unique per distinct operation (UUIDs are recommended) * Keys are scoped per API key — different API keys can use the same idempotency key without conflict * Cached responses expire after **24 hours** ## Response Headers When a cached response is replayed, the API sets: ``` Idempotent-Replayed: true ``` You can use this header to detect replayed responses in your application or logging. ## Error Handling ### Key Reuse with Different Parameters 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: ```json theme={"theme":"night-owl"} { "error": { "code": "idempotency_key_reused", "message": "This Idempotency-Key has already been used with different request parameters." } } ``` ### Key Too Long Keys longer than 255 characters return a `400` error: ```json theme={"theme":"night-owl"} { "error": { "code": "invalid_request", "message": "Idempotency-Key must be 255 characters or less." } } ``` ### Server Errors `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. ## SDK Usage The Spree SDK supports idempotency keys via the `idempotencyKey` option: ```typescript theme={"theme":"night-owl"} import { createClient } from '@spree/sdk' const client = createClient({ baseUrl: 'http://localhost:3000', publishableKey: 'pk_xxx', }) // Add item to cart with idempotency key await client.carts.items.create(cartId, { variant_id: 'variant_xxx', quantity: 1, }, { idempotencyKey: '550e8400-e29b-41d4-a716-446655440000', }) ``` ## Best Practices * **Generate a new key for each distinct operation.** Use a UUID (v4) or another random identifier. Never reuse keys across different operations. * **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. * **Don't use idempotency keys for GET requests.** GET requests are naturally idempotent and the API ignores the header on read-only endpoints. * **Let keys expire naturally.** Cached responses expire after 24 hours. Don't rely on idempotency keys for longer-term deduplication. ## Relationship with Optimistic Locking The Store API also uses [optimistic locking](/docs/api-reference/store-api/introduction) via the `state_lock_version` field on orders. These mechanisms complement each other: | Mechanism | Prevents | Scope | | --------------------------------------------- | --------------------------------- | ---------------------------------- | | **Idempotency keys** | Duplicate operations from retries | Per-request deduplication | | **Optimistic locking** (`state_lock_version`) | Concurrent modifications | Conflict detection between clients | 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. # Store API Source: https://spreecommerce.org/docs/api-reference/store-api/introduction This API reference includes Spree Store APIs, which are REST APIs exposed by the Spree application. They are used to create a storefront for your commerce store, such as a website, point of sale or a commerce mobile app. All API Routes are prefixed with `/api/v3/store`. So, during development, the API Routes will be available under the path `http://localhost:3000/api/v3/store`. For production, replace `http://localhost:3000` with your Spree application URL. ## Using SDK We recommend using the Spree SDK to interact with the Store API. The SDK provides a convenient way to make API requests and handle responses. ### Installation ```bash theme={"theme":"night-owl"} npm install @spree/sdk # or yarn add @spree/sdk # or pnpm add @spree/sdk ``` ### Quick Start ```typescript theme={"theme":"night-owl"} import { createClient } from '@spree/sdk'; // Initialize the client const client = createClient({ baseUrl: 'http://localhost:3000', publishableKey: 'pk_xxx' }); ``` # Localization Source: https://spreecommerce.org/docs/api-reference/store-api/localization How to set locale, currency, and country for API requests The Store API supports multi-language, multi-currency, and market-aware responses. Use request headers to control the locale, currency, and country for each request. ## Headers | Header | Example | Description | | ------------------ | ------- | ------------------------------------------------------------------- | | `X-Spree-Locale` | `fr` | Language for translated content (product names, descriptions, etc.) | | `X-Spree-Currency` | `EUR` | Currency for prices and totals | | `X-Spree-Country` | `FR` | Country ISO code for market resolution | ## Client-Level Defaults Set locale, currency, and country once at initialization — all subsequent requests use these defaults: ```typescript SDK theme={"theme":"night-owl"} import { createClient } from '@spree/sdk' const client = createClient({ baseUrl: 'https://api.mystore.com', publishableKey: 'pk_xxx', locale: 'fr', currency: 'EUR', country: 'FR', }) // All requests use fr/EUR/FR automatically const products = await client.products.list() ``` Update defaults at any time: ```typescript theme={"theme":"night-owl"} client.setLocale('de') client.setCurrency('EUR') client.setCountry('DE') ``` ## Per-Request Overrides Override client defaults for individual requests: ```typescript SDK theme={"theme":"night-owl"} const products = await client.products.list({}, { locale: 'fr', currency: 'EUR', country: 'FR', }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Locale: fr' \ -H 'X-Spree-Currency: EUR' ``` ## Country and Markets The `X-Spree-Country` header resolves the customer's [market](/docs/developer/core-concepts/markets), which influences default locale and currency. Markets allow you to configure different pricing, languages, and product availability per region. ```typescript SDK theme={"theme":"night-owl"} // Setting country automatically resolves the market const products = await client.products.list({}, { country: 'DE', // Resolves German market → EUR, de locale }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Country: DE' ``` When a market is resolved from the country: 1. The market's default locale is used (unless overridden by `X-Spree-Locale`) 2. The market's default currency is used (unless overridden by `X-Spree-Currency`) You can also resolve the market explicitly using the [Markets API](/docs/developer/sdk/store/markets): ```typescript SDK theme={"theme":"night-owl"} // Resolve which market applies for a country const market = await client.markets.resolve('DE') // => { id: "mkt_xxx", name: "Europe", currency: "EUR", default_locale: "de", ... } // Then set the client defaults client.setLocale(market.default_locale) client.setCurrency(market.currency) client.setCountry('DE') ``` ```bash cURL theme={"theme":"night-owl"} # Resolve market by country curl 'http://localhost:3000/api/v3/store/markets/resolve?country=DE' \ -H 'X-Spree-Api-Key: pk_xxx' ``` ## Resolution Priority Each value is resolved in the following order: ### Locale 1. `X-Spree-Locale` header 2. `locale` query parameter 3. Market default locale (if country is set) 4. Store default locale ### Currency 1. `X-Spree-Currency` header 2. `currency` query parameter 3. Market default currency (if country is set) 4. Store default currency ### Market 1. `X-Spree-Country` header 2. `country` query parameter The locale and currency must be supported by the current store. If an unsupported value is provided, the API falls back to the store's default. ## Translated Content When a locale is set, all translatable fields are returned in the requested language. This includes product names, descriptions, category names, and other content managed through [translations](/docs/developer/core-concepts/translations). ```typescript SDK theme={"theme":"night-owl"} // English (default) const enProduct = await client.products.get('spree-tote') console.log(enProduct.name) // "Spree Tote" // French const frProduct = await client.products.get('spree-tote', {}, { locale: 'fr', }) console.log(frProduct.name) // "Sac Spree" ``` ```bash cURL theme={"theme":"night-owl"} # English curl 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Locale: en' # French curl 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Locale: fr' ``` If a translation doesn't exist for the requested locale, the API falls back to the store's default locale automatically. ## Currency-Aware Prices All price fields in the response reflect the requested currency: ```typescript SDK theme={"theme":"night-owl"} // USD prices const usdProduct = await client.products.get('spree-tote', {}, { currency: 'USD', }) console.log(usdProduct.price) // { amount: "15.99", currency: "USD" } // EUR prices const eurProduct = await client.products.get('spree-tote', {}, { currency: 'EUR', }) console.log(eurProduct.price) // { amount: "14.49", currency: "EUR" } ``` ```bash cURL theme={"theme":"night-owl"} # USD prices curl 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Currency: USD' # EUR prices curl 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Currency: EUR' ``` ## Discovering Available Options Use the dedicated endpoints to discover which markets, countries, locales, and currencies are available. These are derived from your store's [markets](/docs/developer/core-concepts/markets) configuration. ### Markets List all markets to get the full picture of regions, currencies, and supported locales: ```typescript SDK theme={"theme":"night-owl"} const { data: markets } = await client.markets.list() // [ // { id: "mkt_xxx", name: "North America", currency: "USD", default_locale: "en", // supported_locales: ["en", "es"], countries: [{ iso: "US", ... }, { iso: "CA", ... }] }, // { id: "mkt_yyy", name: "Europe", currency: "EUR", default_locale: "de", // supported_locales: ["de", "en", "fr"], countries: [{ iso: "DE", ... }, { iso: "FR", ... }] }, // ] ``` ```bash cURL theme={"theme":"night-owl"} curl 'http://localhost:3000/api/v3/store/markets' \ -H 'X-Spree-Api-Key: pk_xxx' ``` ### Countries List all countries or countries within a specific market. Each country includes its market's currency and supported locales: ```typescript SDK theme={"theme":"night-owl"} // All countries across all markets const { data: countries } = await client.countries.list() // Countries in a specific market (useful for checkout address forms) const { data: marketCountries } = await client.markets.countries.list('mkt_xxx') // Get a single country with states const usa = await client.countries.get('US', { expand: ['states'], }) console.log(usa.states) // [{ abbr: "CA", name: "California" }, ...] ``` ```bash cURL theme={"theme":"night-owl"} # All countries curl 'http://localhost:3000/api/v3/store/countries' \ -H 'X-Spree-Api-Key: pk_xxx' # Countries in a specific market curl 'http://localhost:3000/api/v3/store/markets/mkt_xxx/countries' \ -H 'X-Spree-Api-Key: pk_xxx' # Get a country with states curl 'http://localhost:3000/api/v3/store/countries/US?expand=states' \ -H 'X-Spree-Api-Key: pk_xxx' ``` ### Locales ```typescript SDK theme={"theme":"night-owl"} const { data: locales } = await client.locales.list() // [{ code: "en", name: "English" }, { code: "fr", name: "French" }, ...] ``` ```bash cURL theme={"theme":"night-owl"} curl 'http://localhost:3000/api/v3/store/locales' \ -H 'X-Spree-Api-Key: pk_xxx' ``` ### Currencies ```typescript SDK theme={"theme":"night-owl"} const { data: currencies } = await client.currencies.list() // [{ iso_code: "USD", name: "US Dollar", symbol: "$" }, { iso_code: "EUR", name: "Euro", symbol: "€" }, ...] ``` ```bash cURL theme={"theme":"night-owl"} curl 'http://localhost:3000/api/v3/store/currencies' \ -H 'X-Spree-Api-Key: pk_xxx' ``` # Get a country Source: https://spreecommerce.org/docs/api-reference/store-api/markets/get-a-country /api-reference/store.yaml get /api/v3/store/countries/{iso} Returns a single country by ISO code. Supports ?expand=states for address forms and ?expand=market for market details. # Get a country in a market Source: https://spreecommerce.org/docs/api-reference/store-api/markets/get-a-country-in-a-market /api-reference/store.yaml get /api/v3/store/markets/{market_id}/countries/{id} Returns a single country by ISO code within a market. Supports ?expand=states for address forms. # Get a market Source: https://spreecommerce.org/docs/api-reference/store-api/markets/get-a-market /api-reference/store.yaml get /api/v3/store/markets/{id} Returns a single market by prefixed ID with its countries, currency, locales, and tax configuration. # List countries Source: https://spreecommerce.org/docs/api-reference/store-api/markets/list-countries /api-reference/store.yaml get /api/v3/store/countries Returns countries available in the store. Use ?expand=market to include market details (currency, locale, tax_inclusive). # List countries in a market Source: https://spreecommerce.org/docs/api-reference/store-api/markets/list-countries-in-a-market /api-reference/store.yaml get /api/v3/store/markets/{market_id}/countries Returns countries belonging to a specific market. Use this for address form country dropdowns during checkout. # List markets Source: https://spreecommerce.org/docs/api-reference/store-api/markets/list-markets /api-reference/store.yaml get /api/v3/store/markets Returns all markets for the current store with their countries, currency, locales, and tax configuration. # List supported currencies Source: https://spreecommerce.org/docs/api-reference/store-api/markets/list-supported-currencies /api-reference/store.yaml get /api/v3/store/currencies Returns currencies supported by the store (derived from markets) # List supported locales Source: https://spreecommerce.org/docs/api-reference/store-api/markets/list-supported-locales /api-reference/store.yaml get /api/v3/store/locales Returns locales supported by the store (derived from markets) # Resolve market by country Source: https://spreecommerce.org/docs/api-reference/store-api/markets/resolve-market-by-country /api-reference/store.yaml get /api/v3/store/markets/resolve Determine which market applies for a given country ISO code. Useful for auto-selecting the correct currency and locale when a customer's location is known. # Metadata Source: https://spreecommerce.org/docs/api-reference/store-api/metadata How to store and retrieve custom metadata on carts, orders, and line items The Store API supports storing arbitrary key-value **metadata** on carts (orders) and line items. Metadata is useful for tracking custom information like gift messages, attribution data, or integration-specific fields. Metadata is different from [metafields](/docs/developer/core-concepts/metafields). Metadata is simple JSON storage (untyped, no schema), while metafields are structured, typed, and schema-defined. Use metadata for integration data and internal tracking. Use metafields for customer-facing custom attributes. ## Cart Metadata You can attach metadata when creating a cart: ```typescript SDK theme={"theme":"night-owl"} const cart = await client.carts.create({ metadata: { utm_source: 'google', utm_campaign: 'summer_sale', referral_code: 'FRIEND20', }, }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X POST 'http://localhost:3000/api/v3/store/carts' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Content-Type: application/json' \ -d '{ "metadata": { "utm_source": "google", "utm_campaign": "summer_sale", "referral_code": "FRIEND20" } }' ``` ## Cart Metadata (Update) Update metadata on an existing cart: ```typescript SDK theme={"theme":"night-owl"} await client.carts.update(cart.id, { metadata: { gift_message: 'Happy Birthday!', preferred_delivery: 'morning', }, }, { spreeToken: cart.token }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X PATCH 'http://localhost:3000/api/v3/store/carts/cart_xxx' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Token: ' \ -H 'Content-Type: application/json' \ -d '{ "metadata": { "gift_message": "Happy Birthday!", "preferred_delivery": "morning" } }' ``` ## Item Metadata Attach metadata to individual items when adding or updating them: ### When Adding an Item ```typescript SDK theme={"theme":"night-owl"} await client.carts.items.create(cart.id, { variant_id: 'variant_abc123', quantity: 1, metadata: { personalization: 'John', gift_wrap: true, }, }, { spreeToken: cart.token }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X POST 'http://localhost:3000/api/v3/store/carts/cart_xxx/items' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Token: ' \ -H 'Content-Type: application/json' \ -d '{ "variant_id": "variant_abc123", "quantity": 1, "metadata": { "personalization": "John", "gift_wrap": true } }' ``` ### When Updating an Item Updating metadata **merges** with existing values rather than replacing them: ```typescript SDK theme={"theme":"night-owl"} await client.carts.items.update(cart.id, 'li_xyz789', { metadata: { personalization: 'Jane', // Updates existing key engraving: 'With Love', // Adds new key }, }, { spreeToken: cart.token }) ``` ```bash cURL theme={"theme":"night-owl"} curl -X PATCH 'http://localhost:3000/api/v3/store/carts/cart_xxx/items/li_xyz789' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'X-Spree-Token: ' \ -H 'Content-Type: application/json' \ -d '{ "metadata": { "personalization": "Jane", "engraving": "With Love" } }' ``` Metadata values can be any JSON-serializable type: strings, numbers, booleans, arrays, or nested objects. ## Metadata Structure Metadata is stored as a flat JSON object. You can use any keys and values: ```json theme={"theme":"night-owl"} { "metadata": { "string_value": "hello", "number_value": 42, "boolean_value": true, "nested_object": { "key": "value" } } } ``` ## Metadata vs Metafields Spree has two permanent, complementary systems for custom data — **metadata for machines, metafields for humans**. | | Metadata | Metafields | | -------------- | --------------------------------------------------------- | ------------------------------------------------------ | | **Purpose** | Developer escape hatch — integration IDs, sync state | Merchant-defined structured attributes | | **Schema** | Schemaless JSON — no definition required | Defined via MetafieldDefinitions (typed, validated) | | **Validation** | None | Type-specific validation | | **Visibility** | Write-only in Store API, readable in Admin API | Configurable (admin-only or public) | | **Admin UI** | JSON preview | Dedicated form fields | | **Best for** | Integration data, tracking, attribution, write-and-forget | Product specs, custom attributes, customer-facing data | | **API access** | Write via Store API, read via Admin API | Read/write via both APIs | Both systems are here to stay. For more details on metafields, see the [Metafields guide](/docs/developer/core-concepts/metafields). # Migrating from Storefront API v2 Source: https://spreecommerce.org/docs/api-reference/store-api/migrating-from-storefront-api-v2 Map every Storefront API v2 endpoint, concept, and SDK call to its Store API v3 equivalent. The Store API is the successor to the [legacy Storefront API](/docs/api-reference/v2/introduction). It exposes the same surface — products, carts, checkout, customers, wishlists — but with a new transport (flat JSON instead of JSON:API), a new [fully-typed TypeScript SDK](/docs/developer/sdk/quickstart), and resource-oriented routes that line up with the new [Admin API](/docs/api-reference/admin-api). It's faster, safer and easier to work with. This guide walks you through the differences and gives you a one-to-one mapping you can grep against during a migration. API v2 is available via [spree\_legacy\_api\_v2](https://github.com/spree/spree_legacy_api_v2) gem and will work with Spree 5. However new features such as [Markets](/docs/developer/core-concepts/markets) or [new Pricing engine](/docs/developer/core-concepts/pricing) are only available in API v3. ## TL;DR | | **Storefront API v2** | **Store API v3** | | ---------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | Path prefix | `/api/v2/storefront/*` | `/api/v3/store/*` | | Response format | [JSON:API](https://jsonapi.org/) (`data` / `attributes` / `relationships` / `included`) | Flat JSON (attributes inlined on the resource) | | ID format | Numeric (`123`) or slug | Prefixed string (`prod_86Rf07xd4z`, `cart_k5nR8xLq`) | | Routing style | Action-based (`/cart/add_item`, `/checkout/next`) | RESTful resources (`POST /carts/:id/items`, no checkout state machine) | | Filtering | `filter[...]` params, fixed per endpoint | [Ransack](/docs/api-reference/store-api/querying) via `q[...]`, plus per-endpoint scopes | | Including associations | `?include=variants,images` | `?expand=variants,media` (single param, dot-nested up to 4 levels) | | API key | *(none — fully public)* | `X-Spree-Api-Key: pk_xxx` *(publishable key, required on every request)* | | Cart token header | `X-Spree-Order-Token` | `X-Spree-Token` | | Customer auth | `Authorization: Bearer ` (OAuth via `/spree_oauth/token`) | `Authorization: Bearer ` (JWT via `/auth/login`) | | Checkout model | Step state machine (`address → delivery → payment → confirm → complete`) | Stateless cart + nested resources (addresses, payments, payment sessions) | | SDK package | [`@spree/storefront-api-v2-sdk`](https://www.npmjs.com/package/@spree/storefront-api-v2-sdk) *(deprecated)* | [`@spree/sdk`](https://www.npmjs.com/package/@spree/sdk) | | SDK factory | `makeClient({ host })` | `createClient({ baseUrl, publishableKey })` | | SDK response | `Result` with `.success()` / `.fail()` | Returns the resource directly; throws `SpreeError` on failure | | Type safety | Hand-written interfaces | Auto-generated TypeScript types + Zod runtime validators | ## Mental model: what changed and why ### v2 — JSON:API with action endpoints Storefront v2 was modelled on JSON:API. Every response had `data`/`attributes`/`relationships`/`included`, and most non-`GET` calls invoked a named action on a singleton resource (`/cart/add_item`, `/checkout/next`, `/checkout/select_shipping_method`). The current cart was implicit — the server resolved it from the `X-Spree-Order-Token` header. Checkout was a five-step state machine; the SPA's job was to drive `PATCH /checkout/next` until the order reached `complete`. This made cart and checkout calls easy to write but hard to reason about. The same payload could land you in different checkout states depending on which step the order happened to be on, and refactoring the front-end meant knowing which actions transitioned which states. ### v3 — REST with explicit resources Store API v3 collapses checkout into the cart. There is no checkout state machine, no `/checkout/next`, no `/checkout/advance`. Instead: * The cart is a real resource with a stable prefixed ID (`cart_…`). You `PATCH /carts/:id` to attach an email or addresses, and you `POST /carts/:id/items` to add line items. * Delivery rates and payment methods are not separate endpoints — fulfillments are nested under the cart (`PATCH /carts/:id/fulfillments/:fid` to pick a delivery rate), and payments are nested under the cart (`POST /carts/:id/payments` for non-session methods, `POST /carts/:id/payment_sessions` for Stripe/PayPal/Adyen). * Completing checkout is a single explicit call: `POST /carts/:id/complete`. It returns the resulting `Order`. The cart can be created, edited, abandoned, completed, and associated with a user from a single URL. No step transitions, no implicit current cart. This makes it possible to ship Shopify-style one-page checkout without fighting the API. ### Why JSON:API is gone JSON:API's strengths — sparse fieldsets, relationship graphs, normalized payloads — are real, but most storefront clients flattened the response anyway. The cost was a noisy, hard-to-cache wire format and a two-step deserialization on every call. v3 returns the resource directly with associations inlined when `expand` is requested: ```json v2 (JSON:API) theme={"theme":"night-owl"} { "data": { "id": "96", "type": "product", "attributes": { "name": "Bomber Jacket", "slug": "bomber-jacket", "available_on": "2021-10-02T11:02:29.288Z", "purchasable": true, "in_stock": true, "currency": "USD", "price": "38.99", "display_price": "$38.99", "compare_at_price": null, "display_compare_at_price": null }, "relationships": { "variants": { "data": [{ "id": "212", "type": "variant" }] }, "default_variant": { "data": { "id": "212", "type": "variant" } } } }, "included": [ { "id": "212", "type": "variant", "attributes": { "sku": "JacketsandCoats_bomberjacket_38.99", "price": "38.99" } } ] } ``` ```json v3 (Flat JSON, with ?expand=variants) theme={"theme":"night-owl"} { "id": "prod_UkLWZg9DAJ", "name": "Bomber Jacket", "slug": "bomber-jacket", "available_on": "2025-05-13T22:27:22.136Z", "purchasable": true, "in_stock": true, "description": "Dolorem nulla odit nostrum placeat...", "description_html": "

Dolorem nulla odit nostrum placeat...

", "default_variant_id": "variant_gbHJdmfrXB", "thumbnail_url": null, "tags": [], "price": { "id": "price_gbHJdmfrXB", "amount": "38.99", "amount_in_cents": 3899, "currency": "USD", "display_amount": "$38.99", "compare_at_amount": null, "compare_at_amount_in_cents": null, "display_compare_at_amount": null }, "original_price": null, "variants": [ { "id": "variant_gbHJdmfrXB", "product_id": "prod_UkLWZg9DAJ", "sku": "JacketsandCoats_bomberjacket_38.99", "options_text": "Size: M", "purchasable": true, "in_stock": true, "price": { "amount": "38.99", "amount_in_cents": 3899, "currency": "USD", "display_amount": "$38.99" } } ] } ```
You can still ask for sparse fields (`?fields=name,price`), and you still control association depth (`?expand=variants.media`), but you no longer have to walk `included` to assemble the response. See [Querying](/docs/api-reference/store-api/querying) and [Relations](/docs/api-reference/store-api/relations). ### Prefixed IDs everywhere Every v3 resource has a Stripe-style prefixed ID — `prod_…`, `variant_…`, `cart_…`, `ord_…`, `addr_…`. The prefix is part of the public surface: pass it back exactly as received, never strip the prefix or cast it to an integer. (Internally, IDs are still numeric, but the API only ever exposes the prefixed form.) See the [Introduction](/docs/api-reference/store-api/introduction). ## SDK: `@spree/storefront-api-v2-sdk` → `@spree/sdk` The two SDKs cover the same ground but differ in shape. The legacy `@spree/storefront-api-v2-sdk` uses a `makeClient` factory, exposes resource namespaces (`account`, `cart`, `checkout`, `products`, `taxons`, `wishlists`), wraps every response in a `Result` envelope, and passes tokens via an `IToken` (`{ orderToken, bearerToken }`) argument on every method. `@spree/sdk` uses a `createClient` factory, lines its resource namespaces up with the REST tree (`products`, `categories`, `carts`, `carts.items`, `customer.orders`, …), returns the resource directly (no `Result` wrapper), and threads auth through a per-call `RequestOptions` (`{ token, spreeToken }`) — the publishable key is set once at client construction. ### Installing ```bash @spree/sdk (v3) theme={"theme":"night-owl"} npm install @spree/sdk # or pnpm add @spree/sdk ``` ```bash @spree/storefront-api-v2-sdk (v2 — deprecated) theme={"theme":"night-owl"} npm install @spree/storefront-api-v2-sdk ``` ### Creating a client ```typescript v3 — @spree/sdk theme={"theme":"night-owl"} import { createClient } from '@spree/sdk' const client = createClient({ baseUrl: 'https://your-store.com', publishableKey: 'pk_xxx', }) ``` ```typescript v2 — @spree/storefront-api-v2-sdk theme={"theme":"night-owl"} import { makeClient } from '@spree/storefront-api-v2-sdk' const client = makeClient({ host: 'https://your-store.com', }) // No API key — the Storefront API v2 was fully public. ``` The Storefront API v2 had no API key concept — anyone with the host could call it. Store API v3 introduces a **publishable key** (`pk_xxx`) that's required on every request and identifies which store the call targets. The key is safe to expose in client-side code; it's how v3 supports multi-store on a single domain and gives you per-key rate limits, scopes, and audit trails. ### Calling an endpoint ```typescript v3 — @spree/sdk theme={"theme":"night-owl"} // Returns the Product directly — throws on failure const product = await client.products.get('spree-tote', { expand: ['variants', 'media'], }) console.log(product.name, product.price) ``` ```typescript v2 — @spree/storefront-api-v2-sdk theme={"theme":"night-owl"} // Returns a Result wrapper const response = await client.products.show({ id: 'spree-tote', include: 'variants,images', }) if (response.isSuccess()) { const { data, included } = response.success() console.log(data.attributes.name, data.attributes.price) // Walk `included` to resolve relationships… } else { console.error(response.fail()) } ``` ### Auth tokens ```typescript v3 — @spree/sdk theme={"theme":"night-owl"} // Guest cart (uses cart's order token as `spreeToken`) const cart = await client.carts.create() await client.carts.items.create( cart.id, { variant_id: 'variant_abc', quantity: 1 }, { spreeToken: cart.token }, ) // Authenticated customer (JWT) const { token } = await client.auth.login({ email: 'me@example.com', password: 'spree123', }) const orders = await client.customer.orders.list({}, { token }) ``` ```typescript v2 — @spree/storefront-api-v2-sdk theme={"theme":"night-owl"} // Guest cart (orderToken) const cartResponse = await client.cart.create() const orderToken = cartResponse.success().data.attributes.token await client.cart.addItem({ order_token: orderToken, }, { variant_id: 10, quantity: 1 }) // Authenticated customer (OAuth via /spree_oauth/token) const tokenResponse = await client.authentication.getToken({ username: 'me@example.com', password: 'spree123', }) const bearer = tokenResponse.success().access_token const ordersResponse = await client.account.ordersList({ bearer_token: bearer, }) ``` In v3, `token` and `spreeToken` are passed via the `RequestOptions` object on each call — no more per-method `bearer_token` / `order_token` arguments mixed into the body. JWT refresh uses `client.auth.refresh({ refresh_token })`; the old OAuth `refresh_token` grant against `/spree_oauth/token` is gone. ### Error handling In v3 the SDK throws a `SpreeError` instance with `code`, `status`, and `details` properties. Wrap calls in `try/catch` or let them bubble. The `Result` wrapper from v2 is gone — code that branched on `response.isSuccess()` becomes a single happy path plus a `catch`. ### TypeScript types v3 ships generated TypeScript types and runtime [Zod](https://zod.dev) schemas that stay in lockstep with the API — every response field is typed, and you can validate payloads at runtime where you need belt-and-braces safety (form submissions, untrusted webhooks). v2's types were hand-maintained interfaces inside the SDK, which drifted from the actual responses over time. ## Endpoint mapping The tables below cover every public path in `/api/v2/storefront/*` and where to find its v3 equivalent. Anything not listed is unchanged in scope but follows the new conventions (flat JSON, prefixed IDs, Ransack filters). ### Catalog: products, taxons, categories | Storefront API v2 | Store API v3 | Notes | | ------------------------------ | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `GET /products` | `GET /products` | Filters move from `filter[...]` to [Ransack `q[...]`](/docs/api-reference/store-api/querying); `include` → `expand`. | | `GET /products/:slug` | `GET /products/:id_or_slug` | Accepts prefixed ID or slug. | | `GET /products/:slug/variants` | `GET /products/:id?expand=variants` | Variants are returned via `expand`, not as a separate route. | | `GET /taxons` | `GET /categories` | **Renamed.** v3 calls them Categories everywhere — same tree model, same `permalink`, parametrised by slug or prefixed ID. | | `GET /taxons/:id` | `GET /categories/:id_or_permalink` | Permalinks containing slashes (`clothing/shirts`) work as-is. | | *(new)* | `GET /products/filters` | Returns price range, in-stock toggle, option values, and category facets with counts — designed for filter sidebars. | This is an area where API v3 has the biggest performance advantage over v2. `GET /products` by default will expose `default_variant_id`, `thumbnail_url` and `price` which are essential for building product lists. You don't need to expand `variants` or `media` (images) like with API v2. ### Cart and checkout This is the biggest conceptual change. The v2 cart was a singleton accessed via the order token header; v3 carts have prefixed IDs and live alongside line items, payments, fulfillments, and discount codes as nested resources. **There is no checkout state machine in v3.** Backend will handle that automatically, without any developer action needed. This aligns with [Spree 6 upcoming changes](https://github.com/spree/spree/issues/13930). By default all Cart endpoints will return all associations auto-expanded. | Storefront API v2 | Store API v3 | Notes | | ------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | | `POST /cart` | `POST /carts` | Returns a `Cart` with a prefixed `id` and a `token` (use as `X-Spree-Token` for guests). | | `GET /cart` | `GET /carts/:id` | Pass the prefixed `id`. Authenticated users can `GET /carts` to list active carts. | | `DELETE /cart` | `DELETE /carts/:id` | Same semantics. | | `POST /cart/add_item` | `POST /carts/:id/items` | Nested resource, not an action. | | `PATCH /cart/set_quantity` | `PATCH /carts/:id/items/:line_item_id` | Updates an explicit line item by ID. | | `DELETE /cart/set_quantity` | `DELETE /carts/:id/items/:line_item_id` | Same. | | `PATCH /cart/empty` | Iterate `DELETE /carts/:id/items/:line_item_id` | No bulk-empty action; remove line items individually, or `DELETE /carts/:id` to abandon. | | `PATCH /cart/apply_coupon_code` | `POST /carts/:id/discount_codes` | Body: `{ code }`. | | `DELETE /cart/apply_coupon_code` | `DELETE /carts/:id/discount_codes/:code` | Path-level code. | | `DELETE /cart/remove_coupon_code` | Iterate `DELETE /carts/:id/discount_codes/:code` | No remove-all shortcut; remove each code. | | `GET /cart/estimate_shipping_rates` | Inspect `cart.fulfillments[].delivery_rates` | Rates are returned inline with the cart. Add an address (`PATCH /carts/:id`) and the cart is recomputed. | | `PATCH /cart/associate` | `PATCH /carts/:id/associate` | Pass the JWT for the now-authenticated user. | | `PATCH /cart/change_currency` | `PATCH /carts/:id` | Set `currency` directly on the cart. | | `PATCH /checkout` | `PATCH /carts/:id` | Email, addresses, special instructions, etc. — all on the cart. | | `PATCH /checkout/next` | *(removed)* | No state machine; nothing to advance. | | `PATCH /checkout/advance` | *(removed)* | Same. | | `PATCH /checkout/complete` | `POST /carts/:id/complete` | Returns the resulting `Order`. | | `PATCH /checkout/select_shipping_method` | `PATCH /carts/:id/fulfillments/:fulfillment_id` | Body: `{ selected_delivery_rate_id }`. ShippingMethod is the legacy term — v3 calls them Delivery Methods / Delivery Rates. | | `POST /checkout/validate_order_for_payment` | *(removed)* | Validation happens server-side when you call `complete`. | | `POST /checkout/create_payment` | `POST /carts/:id/payments` | For offline / non-session methods (cash, check, bank transfer). | | `POST /checkout/add_store_credit` | `POST /carts/:id/store_credits` | Body: `{ amount? }`. | | `POST /checkout/remove_store_credit` | `DELETE /carts/:id/store_credits` | Same. | | `GET /checkout/payment_methods` | `cart.available_payment_methods` | Inlined on the cart. | | `GET /checkout/shipping_rates` | `cart.fulfillments[].delivery_rates` | Same — inlined. | The new RESTful design allows you to implement different usage scenarios like multiple saved carts per customer or organization (company). #### Session-based payments (Stripe, Adyen, PayPal) API v2 had per-gateway endpoints (`/stripe/payment_intents`, `/adyen/payment_sessions`). API v3 unifies these behind a generic [Payment Sessions API](/docs/developer/core-concepts/payments#session-based-flow-stripe-adyen-paypal-etc) — the gateway-specific payload moves into the request body, and Spree dispatches to the right provider based on the `payment_method_id`. This shortens the integration time and allows teams to deliver payment integrations faster. Also your frontend code doesn't need to change per gateway. | Storefront API v2 | Store API v3 | | --------------------------------------------- | ---------------------------------------------------------------------- | | `POST /stripe/payment_intents` | `POST /carts/:id/payment_sessions` | | `GET /stripe/payment_intents/:id` | `GET /carts/:id/payment_sessions/:id` | | `PATCH /stripe/payment_intents/:id` | `PATCH /carts/:id/payment_sessions/:id` | | `PATCH /stripe/payment_intents/:id` (confirm) | `PATCH /carts/:id/payment_sessions/:id/complete` | | `POST /stripe/setup_intents` | `POST /customers/me/payment_setup_sessions` (save card for future use) | | `POST /adyen/payment_sessions` | `POST /carts/:id/payment_sessions` | | `POST /adyen/payment_sessions/:id/complete` | `PATCH /carts/:id/payment_sessions/:id/complete` | ### Customer account API v2 exposed a singleton `/account` endpoint with OAuth tokens minted at `/spree_oauth/token`. API v3 splits the surface into a public registration endpoint (`POST /customers`) and a `/customers/me` namespace for the authenticated customer. Auth moves from OAuth to JWT (`POST /auth/login`). | Storefront API v2 | Store API v3 | Notes | | ------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------- | | `POST /account` | `POST /customers` | Returns JWT tokens on success. | | `GET /account` | `GET /customers/me` | | | `PATCH /account` | `PATCH /customers/me` | `current_password` required to change email or password. | | `GET /account/addresses` | `GET /customers/me/addresses` | | | `POST /account/addresses` | `POST /customers/me/addresses` | | | `PATCH /account/addresses/:id` | `PATCH /customers/me/addresses/:id` | | | `DELETE /account/addresses/:id` | `DELETE /customers/me/addresses/:id` | | | `GET /account/credit_cards` | `GET /customers/me/credit_cards` | | | `GET /account/credit_cards/default` | `GET /customers/me/credit_cards?q[default_eq]=true` | Use a Ransack filter; there's no `/default` shortcut. | | `DELETE /account/credit_cards/:id` | `DELETE /customers/me/credit_cards/:id` | | | `GET /account/orders` | `GET /customers/me/orders` | | | `GET /account/orders/:number` | `GET /customers/me/orders/:id` | Use prefixed ID or order number. | | `GET /order_status/:number` | `GET /orders/:id` | Guest-accessible with the order token; no separate status endpoint. | | *(POST /spree\_oauth/token grant=password)* | `POST /auth/login` | Returns a JWT, not an OAuth token. | | *(POST /spree\_oauth/token grant=refresh\_token)* | `POST /auth/refresh` | | | *(none)* | `POST /auth/logout` | Server-side revocation of the refresh token. | | *(none)* | `POST /password_resets` / `PATCH /password_resets/:token` | First-class password reset flow. | ### Geography and store metadata | Storefront API v2 | Store API v3 | Notes | | --------------------------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `GET /countries` | `GET /countries` | | | `GET /countries/:iso` | `GET /countries/:iso` | Use `?expand=states` for the address form. | | `GET /countries/default` | `client.markets.resolve(country)` | The "default country" concept moved into Markets — resolve which market applies to a country, then read `market.default_country`. | | `GET /store` | *(removed from the storefront surface)* | Store identity is conveyed via the publishable key; you don't need to fetch the store record. | | *(none in v2)* | `GET /markets`, `GET /markets/:id`, `GET /markets/:id/countries`, `GET /markets/resolve` | New in v3 — Markets group countries, currency, and locale. See [Localization](/docs/api-reference/store-api/localization). | | *(none in v2)* | `GET /currencies`, `GET /locales` | Enumerate currencies and locales supported by the store. | | `GET /policies` / `GET /policies/:slug` | `GET /policies` / `GET /policies/:id_or_slug` | Same — return policy, privacy, terms, etc. | ### Wishlists | Storefront API v2 | Store API v3 | | ----------------------------------------------- | -------------------------------------------------- | | `GET /wishlists` | `GET /wishlists` | | `POST /wishlists` | `POST /wishlists` | | `GET /wishlists/:token` | `GET /wishlists/:id` | | `PATCH /wishlists/:token` | `PATCH /wishlists/:id` | | `DELETE /wishlists/:token` | `DELETE /wishlists/:id` | | `GET /wishlists/default` | `GET /wishlists?q[is_default_eq]=true` | | `POST /wishlists/:token/add_item` | `POST /wishlists/:wishlist_id/items` | | `PATCH /wishlists/:token/set_item_quantity/:id` | `PATCH /wishlists/:wishlist_id/items/:id` | | `DELETE /wishlists/:token/remove_item/:id` | `DELETE /wishlists/:wishlist_id/items/:id` | | `POST /wishlists/:token/add_items` | Iterate `POST /wishlists/:wishlist_id/items` | | `DELETE /wishlists/:token/remove_items` | Iterate `DELETE /wishlists/:wishlist_id/items/:id` | ### Digital downloads | Storefront API v2 | Store API v3 | | ---------------------- | ---------------------- | | `GET /digitals/:token` | `GET /digitals/:token` | ### Removed without a v3 equivalent A handful of v2 surfaces don't exist in v3: * **Posts** / **Menus** / **CMS Pages** — the blog/CMS surface is not part of v3 or Spree Core anymore. Recommended: use a dedicated CMS like Payload or Strapi ## Migration checklist The mechanical bits, in order: 1. **Install `@spree/sdk` alongside `@spree/storefront-api-v2-sdk`.** They have different package names, so both can coexist while you cut over endpoints incrementally. 2. **Create a publishable API key** in Spree Admin → Settings → API Keys (or via `spree api-key create`). v3 requires it on every request — v2 had no API key concept at all. 3. **Replace `makeClient({ host })` with `createClient({ baseUrl, publishableKey })`** in one entry point at a time. Keep the v2 client wired up for not-yet-migrated calls. 4. **Switch from `Result<…>` to direct returns + try/catch.** Any code that did `if (response.isSuccess()) { response.success() }` becomes a single statement, with errors thrown as `SpreeError`. 5. **Update token handling.** Replace `{ bearer_token, order_token }` per-method arguments with the `{ token, spreeToken }` second-argument `RequestOptions`. JWT tokens come from `client.auth.login` / `client.customers.create`; cart tokens come from `cart.token` on the cart resource. 6. **Convert filters from `filter[...]` to `q[...]`.** Most filters have a direct Ransack equivalent (see the [Querying](/docs/api-reference/store-api/querying) reference). For products specifically, `taxon_ids` → `in_categories`, `name` → `name_cont` or `search`, `price` range → `price_gte` / `price_lte`. 7. **Rewrite cart/checkout calls as resource operations.** This is the deepest change. The cleanest path is to delete your checkout step controller wholesale and rebuild it as a single page that PATCHes the cart and POSTs to nested resources, then calls `complete` at the end. 8. **Stop walking `included`.** Replace JSON:API normalization helpers with direct attribute access. Use `expand` to pull in associations, and accept that they arrive inlined. 9. **Replace numeric IDs and slugs with prefixed IDs.** Update any code that parsed integers out of IDs, stored IDs as numbers in state, or constructed admin links from raw IDs. 10. **Switch the OAuth token endpoints for JWT.** The `/spree_oauth/token` endpoints are no longer the customer auth surface; use `/api/v3/store/auth/login` / `/auth/refresh` / `/auth/logout`. Refresh tokens are rotated on each refresh call, and `logout` revokes the token server-side. For the conceptual changes — flat JSON, RESTful checkout, Markets — give yourself a sprint of breathing room rather than treating the migration as a string-replace. The win on the other side is a smaller, more obvious client surface and a Store API that lines up with the Admin API for full-stack work. ## See also * [Store API — Introduction](/docs/api-reference/store-api/introduction) * [Authentication](/docs/api-reference/store-api/authentication) * [Querying (Ransack, sorting, pagination, expand, fields)](/docs/api-reference/store-api/querying) * [Relations — `expand` rules and nested associations](/docs/api-reference/store-api/relations) * [Localization — Markets, currencies, locales](/docs/api-reference/store-api/localization) * [Errors](/docs/api-reference/store-api/errors) # Monetary Amounts Source: https://spreecommerce.org/docs/api-reference/store-api/monetary-amounts How monetary values are represented in API responses All monetary values in the Store API are returned as **strings** (e.g., `"29.99"`, `"0.0"`), not numbers. This preserves decimal precision and avoids floating-point rounding issues common with JSON numbers. ## Response Format Every monetary field has a corresponding `display_` field that includes currency formatting: ```json theme={"theme":"night-owl"} { "total": "129.99", "display_total": "$129.99", "item_total": "99.99", "display_item_total": "$99.99", "ship_total": "10.00", "display_ship_total": "$10.00", "tax_total": "20.00", "display_tax_total": "$20.00" } ``` ## Affected Types This convention applies to all monetary fields across all resources: | Resource | Monetary Fields | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Order** | `total`, `item_total`, `ship_total`, `tax_total`, `adjustment_total`, `promo_total`, `included_tax_total`, `additional_tax_total` | | **Line Item** | `price`, `total`, `adjustment_total`, `promo_total`, `pre_tax_amount`, `discounted_amount`, `compare_at_amount` | | **Shipment** | `cost` | | **Payment** | `amount` | | **Gift Card** | `amount`, `amount_used`, `amount_authorized`, `amount_remaining` | | **Store Credit** | `amount`, `amount_used`, `amount_remaining` | | **Price** | `amount`, `compare_at_amount` | ## Working with Amounts ```typescript SDK theme={"theme":"night-owl"} const order = await client.orders.get('or_abc123', {}, { token }); // Raw string values order.total; // "129.99" order.display_total; // "$129.99" // Convert to number for calculations const total = parseFloat(order.total); ``` ```javascript JavaScript theme={"theme":"night-owl"} const data = await response.json(); // Use display fields for rendering element.textContent = data.display_total; // "$129.99" // Use raw fields for calculations const total = parseFloat(data.total); ``` Use `display_*` fields for rendering prices in the UI — they are pre-formatted with the correct currency symbol and decimal places based on the order's currency. Use the raw string fields when you need to perform calculations. # Subscribe to the newsletter Source: https://spreecommerce.org/docs/api-reference/store-api/newsletter-subscribers/subscribe-to-the-newsletter /api-reference/store.yaml post /api/v3/store/newsletter_subscribers Subscribes an email address to the newsletter for the current store. Behavior: - If the email is already verified for this store, the existing subscription is returned unchanged. - If the request is unauthenticated (guest), the subscription is created in an unverified state and two events are published: `newsletter_subscriber.subscription_requested` (carrying the `verification_token` and the validated `redirect_url`, intended for headless storefronts that want to send the confirmation email themselves via a webhook handler) and the legacy `newsletter_subscriber.subscribed` lifecycle event (which the bundled `spree_emails` package listens to and uses to send a default confirmation email). The confirmation link should point at `redirect_url?token=` and call `POST /newsletter_subscribers/verify` when the user clicks it. - If the request is authenticated via JWT and the customer's email matches the subscribed email, the subscription is auto-verified and no events are fired — the JWT already proves email ownership, so no confirmation email is needed. The optional `redirect_url` is where the verification token should land on the storefront. The server does not return a validation error when the URL is outside the store's [Allowed Origins](/developer/core-concepts/allowed-origins); instead, the URL is silently omitted from the webhook payload (secure-by-default). When no allow-list is configured on the store, the URL is also omitted. Callers therefore receive the same 201 regardless, and the webhook handler should fall back to the store's storefront URL when `redirect_url` is missing from the payload. Newsletter consent is preserved across registration: if a guest subscribes and later registers with the same email, the existing subscriber record is reused. # Verify a newsletter subscription Source: https://spreecommerce.org/docs/api-reference/store-api/newsletter-subscribers/verify-a-newsletter-subscription /api-reference/store.yaml post /api/v3/store/newsletter_subscribers/verify Confirms a pending newsletter subscription using the verification token sent by email. After successful verification: - The subscriber record is marked verified. - If the subscription is associated with a customer, that customer's `accepts_email_marketing` flag is set to `true`. # Get an order Source: https://spreecommerce.org/docs/api-reference/store-api/orders/get-an-order /api-reference/store.yaml get /api/v3/store/orders/{id} Returns a single completed order by prefixed ID. Accessible via JWT (authenticated users) or order token header (guests). # Get a policy Source: https://spreecommerce.org/docs/api-reference/store-api/policies/get-a-policy /api-reference/store.yaml get /api/v3/store/policies/{id} Returns a single policy by slug or prefixed ID. Includes the full rich text body. # List store policies Source: https://spreecommerce.org/docs/api-reference/store-api/policies/list-store-policies /api-reference/store.yaml get /api/v3/store/policies Returns all policies for the current store (e.g., return policy, privacy policy, terms of service). Policies are managed in Spree Admin and contain rich text content. # Get a category Source: https://spreecommerce.org/docs/api-reference/store-api/product-catalog/get-a-category /api-reference/store.yaml get /api/v3/store/categories/{id} Returns a single category by permalink or prefix ID # Get a product Source: https://spreecommerce.org/docs/api-reference/store-api/product-catalog/get-a-product /api-reference/store.yaml get /api/v3/store/products/{id} Returns a single product by slug or prefix ID # Get product filters Source: https://spreecommerce.org/docs/api-reference/store-api/product-catalog/get-product-filters /api-reference/store.yaml get /api/v3/store/products/filters Returns available filters for products with their options and counts. Use this endpoint to build filter UIs for product listing pages. The filters are context-aware - when a category_id is provided, only filters relevant to products in that category are returned. # List categories Source: https://spreecommerce.org/docs/api-reference/store-api/product-catalog/list-categories /api-reference/store.yaml get /api/v3/store/categories Returns a paginated list of categories for the current store # List products Source: https://spreecommerce.org/docs/api-reference/store-api/product-catalog/list-products /api-reference/store.yaml get /api/v3/store/products Returns a paginated list of active products for the current store # Querying Source: https://spreecommerce.org/docs/api-reference/store-api/querying How to filter, sort, and paginate Store API results using Ransack The Store API uses [Ransack](https://activerecord-hackery.github.io/ransack/) for filtering and sorting collection endpoints. All query parameters are passed via the `q` parameter. ## Filtering Pass filter conditions using the `q` parameter with Ransack predicates: ```typescript SDK theme={"theme":"night-owl"} const products = await client.products.list({ name_cont: 'shirt', price_gte: 20, price_lte: 100, }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ --data-urlencode 'q[name_cont]=shirt' \ --data-urlencode 'q[price_gte]=20' \ --data-urlencode 'q[price_lte]=100' ``` ### Common Predicates | Predicate | Description | SDK | cURL | | ---------- | --------------------------- | -------------------------------- | -------------------------------------------- | | `eq` | Equals | `status_eq: 'active'` | `q[status_eq]=active` | | `not_eq` | Not equals | `status_not_eq: 'draft'` | `q[status_not_eq]=draft` | | `cont` | Contains (case-insensitive) | `name_cont: 'shirt'` | `q[name_cont]=shirt` | | `start` | Starts with | `name_start: 'Spree'` | `q[name_start]=Spree` | | `end` | Ends with | `slug_end: 'tote'` | `q[slug_end]=tote` | | `lt` | Less than | `price_lt: 50` | `q[price_lt]=50` | | `lteq` | Less than or equal | `price_lteq: 50` | `q[price_lteq]=50` | | `gt` | Greater than | `price_gt: 10` | `q[price_gt]=10` | | `gteq` | Greater than or equal | `price_gteq: 10` | `q[price_gteq]=10` | | `in` | In a list | `status_in: ['active', 'draft']` | `q[status_in][]=active&q[status_in][]=draft` | | `null` | Is null | `deleted_at_null: true` | `q[deleted_at_null]=true` | | `not_null` | Is not null | `completed_at_not_null: true` | `q[completed_at_not_null]=true` | | `present` | Is present (not empty) | `description_present: true` | `q[description_present]=true` | | `blank` | Is blank (null or empty) | `description_blank: true` | `q[description_blank]=true` | | `true` | Is true (boolean) | `purchasable_true: 1` | `q[purchasable_true]=1` | | `false` | Is false (boolean) | `purchasable_false: 1` | `q[purchasable_false]=1` | The SDK automatically wraps filter keys in `q[...]` and appends `[]` for array values — just pass flat params. ### Combining Filters Multiple filters are combined with AND logic: ```typescript SDK theme={"theme":"night-owl"} // Products that contain "shirt" AND cost between $20-$100 const products = await client.products.list({ name_cont: 'shirt', price_gteq: 20, price_lteq: 100, }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ --data-urlencode 'q[name_cont]=shirt' \ --data-urlencode 'q[price_gteq]=20' \ --data-urlencode 'q[price_lteq]=100' ``` ### Filtering by Association You can filter by associated model attributes using underscore notation: ```typescript SDK theme={"theme":"night-owl"} // Products in a specific category (includes descendants) const products = await client.products.list({ in_category: 'ctg_abc123', }) // Multiple categories (OR logic, checkbox filters) const products = await client.products.list({ in_categories: ['ctg_abc123', 'ctg_def456'], }) // Categories at a specific depth const categories = await client.categories.list({ depth_eq: 1, }) ``` ```bash cURL theme={"theme":"night-owl"} # Products in a specific category (includes descendants) curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ --data-urlencode 'q[in_category]=ctg_abc123' # Top-level categories only curl -G 'http://localhost:3000/api/v3/store/categories' \ -H 'X-Spree-Api-Key: pk_xxx' \ --data-urlencode 'q[depth_eq]=1' ``` ## Product Scopes Products support additional filter scopes beyond standard Ransack predicates: ```typescript SDK theme={"theme":"night-owl"} const products = await client.products.list({ price_gte: 20, // Minimum price price_lte: 100, // Maximum price with_option_value_ids: ['optval_abc', 'optval_def'], // Filter by option values in_stock: true, // Only in-stock products }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ --data-urlencode 'q[price_gte]=20' \ --data-urlencode 'q[price_lte]=100' \ --data-urlencode 'q[with_option_value_ids][]=optval_abc' \ --data-urlencode 'q[with_option_value_ids][]=optval_def' \ --data-urlencode 'q[in_stock]=true' ``` | Scope | Description | SDK | cURL | | ----------------------- | -------------------------- | --------------------------------------- | --------------------------------------- | | `price_gte` | Minimum price | `price_gte: 20` | `q[price_gte]=20` | | `price_lte` | Maximum price | `price_lte: 100` | `q[price_lte]=100` | | `with_option_value_ids` | Filter by option value IDs | `with_option_value_ids: ['optval_abc']` | `q[with_option_value_ids][]=optval_abc` | | `in_stock` | Only in-stock products | `in_stock: true` | `q[in_stock]=true` | | `out_of_stock` | Only out-of-stock products | `out_of_stock: true` | `q[out_of_stock]=true` | | `search` | Full-text search | `search: 'shirt'` | `q[search]=shirt` | ## Sorting Use the `sort` parameter on any list endpoint. Prefix a field with `-` for descending order (ascending is the default). This follows the [JSON:API sorting convention](https://jsonapi.org/format/#fetching-sorting). ```typescript SDK theme={"theme":"night-owl"} // Sort by price (low to high) const products = await client.products.list({ sort: 'price', }) // Sort by price (high to low) const products = await client.products.list({ sort: '-price', }) // Sort by best selling const products = await client.products.list({ sort: 'best_selling', }) ``` ```bash cURL theme={"theme":"night-owl"} # Sort by price low to high curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'sort=price' # Sort by price high to low curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'sort=-price' ``` ### Product Sort Options | Value | Description | | --------------- | ------------------------------------------------ | | `manual` | Manual sort order (default, uses taxon position) | | `best_selling` | Best selling products first | | `price` | Price low to high | | `-price` | Price high to low | | `name` | Name A-Z | | `-name` | Name Z-A | | `-available_on` | Newest first | | `available_on` | Oldest first | ### Sorting Other Resources The `sort` parameter works on all list endpoints. Use any sortable column name: ```typescript SDK theme={"theme":"night-owl"} // Categories sorted by name const categories = await client.categories.list({ sort: 'name', }) // Customer orders sorted by most recent const orders = await client.customer.orders.list({ sort: '-completed_at', }) ``` ```bash cURL theme={"theme":"night-owl"} # Categories sorted by name curl -G 'http://localhost:3000/api/v3/store/categories' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'sort=name' # Customer orders by most recent curl -G 'http://localhost:3000/api/v3/store/customer/orders' \ -H 'X-Spree-Api-Key: pk_xxx' \ -H 'Authorization: Bearer ' \ -d 'sort=-completed_at' ``` ## Expanding Associations Use the `expand` parameter to include associated resources in the response. Pass a comma-separated list of association names: ```typescript SDK theme={"theme":"night-owl"} const product = await client.products.get('spree-tote', { expand: ['variants', 'media'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=variants,media' ``` ### Nested Expand Use dot notation to expand nested associations (up to 4 levels deep): ```typescript SDK theme={"theme":"night-owl"} // Expand variants and their media in one request const product = await client.products.get('spree-tote', { expand: ['variants.media'], }) // Multiple nested expands const product = await client.products.get('spree-tote', { expand: ['variants.media', 'variants.custom_fields', 'option_types'], }) // Category with nested children const category = await client.categories.get('clothing/shirts', { expand: ['children.children'], // Two levels of subcategories }) ``` ```bash cURL theme={"theme":"night-owl"} # Expand variants with their media curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=variants.media' # Multiple nested expands curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=variants.media,variants.custom_fields,option_types' ``` ### Rules * Maximum depth is **4 levels** (e.g., `a.b.c.d`) * A nested expand automatically includes its parent — `expand=variants.media` expands both `variants` and their `media` * Nested expand only applies to conditional associations (those controlled by `expand`) ## Field Selection Use the `fields` parameter to request only specific fields in the response. This reduces payload size for bandwidth-sensitive clients like mobile apps or server-side rendering. ```typescript SDK theme={"theme":"night-owl"} // Only return name, slug, and price const products = await client.products.list({ fields: ['name', 'slug', 'price', 'display_price'], }) // Works on single resources too const product = await client.products.get('spree-tote', { fields: ['name', 'slug', 'price'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'fields=name,slug,price,display_price' ``` ```json theme={"theme":"night-owl"} { "data": [ { "id": "prod_86Rf07xd4z", "name": "Spree Tote", "slug": "spree-tote", "price": "15.99", "display_price": "$15.99" } ], "meta": { ... } } ``` ### Rules * The `id` field is **always included**, even if not listed in `fields` * Omitting `fields` returns all fields (default behavior) * Field selection applies to the top-level resource only — expanded associations always return their full set of fields * Expanded associations are **always included** in the response regardless of `fields` — `?fields=name&expand=variants` returns `id`, `name`, and `variants` The SDK TypeScript types remain fully typed regardless of field selection. When using `fields`, be aware that only the requested fields will be present at runtime. ## Pagination All collection endpoints return paginated results. Control pagination with `page` and `limit` parameters: ```typescript SDK theme={"theme":"night-owl"} const { data: products, meta } = await client.products.list({ page: 2, limit: 10, }) // meta contains pagination info console.log(meta) // { // page: 2, // limit: 10, // count: 85, // pages: 9, // from: 11, // to: 20, // in: 10, // previous: 1, // next: 3 // } ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'page=2' \ -d 'limit=10' ``` ### Pagination Parameters | Parameter | Default | Max | Description | | --------- | ------- | ----- | -------------------------- | | `page` | `1` | - | Page number | | `limit` | `25` | `100` | Number of records per page | ### Pagination Metadata Collection responses include a `meta` object with pagination info: ```json theme={"theme":"night-owl"} { "data": [...], "meta": { "page": 1, "limit": 25, "count": 85, "pages": 4, "from": 1, "to": 25, "in": 25, "previous": null, "next": 2 } } ``` | Field | Description | | ---------- | ----------------------------------------- | | `page` | Current page number | | `limit` | Records per page | | `count` | Total number of records | | `pages` | Total number of pages | | `from` | Starting record number on this page | | `to` | Ending record number on this page | | `in` | Number of records returned on this page | | `previous` | Previous page number (null if first page) | | `next` | Next page number (null if last page) | # Rate Limiting Source: https://spreecommerce.org/docs/api-reference/store-api/rate-limitting Rate limits and throttling for the Store API The Store API enforces rate limits to protect against abuse and ensure fair usage. Rate limits are applied per API key or per IP address depending on the endpoint. ## Default Limits | Endpoint | Limit | Scope | Window | | -------------------- | ------------ | ----------- | -------- | | All endpoints | 300 requests | Per API key | 1 minute | | `POST /auth/login` | 5 requests | Per IP | 1 minute | | `POST /customers` | 3 requests | Per IP | 1 minute | | `POST /auth/refresh` | 10 requests | Per IP | 1 minute | The global rate limit is tracked by your publishable API key (`X-Spree-Api-Key`). If the key is not provided, the limit falls back to the client's IP address. Authentication endpoints have stricter per-IP limits to prevent brute-force attacks. ## Rate Limit Headers Every Store API response includes headers that show your current rate limit usage: | Header | Description | | ----------------------- | -------------------------------------------------------------------- | | `X-RateLimit-Limit` | Maximum number of requests allowed per window | | `X-RateLimit-Remaining` | Number of requests remaining in the current window | | `Retry-After` | Seconds to wait before retrying (only present when limit is reached) | Example response headers: ``` HTTP/1.1 200 OK X-RateLimit-Limit: 300 X-RateLimit-Remaining: 295 ``` ## Rate Limit Response When you exceed the rate limit, the API returns a `429 Too Many Requests` response: ```json theme={"theme":"night-owl"} { "error": { "code": "rate_limit_exceeded", "message": "Too many requests. Please retry later." } } ``` The response includes rate limit headers and a `Retry-After` header indicating how many seconds to wait before retrying: ``` HTTP/1.1 429 Too Many Requests Content-Type: application/json X-RateLimit-Limit: 300 X-RateLimit-Remaining: 0 Retry-After: 60 ``` ## SDK Retry Handling The Spree SDK automatically handles rate-limited responses with built-in retry logic and exponential backoff: ```typescript theme={"theme":"night-owl"} import { createClient } from '@spree/sdk' const client = createClient({ baseUrl: 'http://localhost:3000', publishableKey: 'pk_xxx', retry: { maxRetries: 2, // Number of retry attempts (default: 2) baseDelay: 300, // Initial delay in ms (default: 300) maxDelay: 10000, // Maximum delay in ms (default: 10000) }, }) ``` The SDK respects the `Retry-After` header and only retries on `429` status codes for non-GET requests. For GET/HEAD requests, it also retries on `500`, `502`, `503`, and `504` errors. ## Configuring Rate Limits If you're self-hosting Spree, you can adjust rate limits in your initializer: ```ruby theme={"theme":"night-owl"} # config/initializers/spree.rb Spree::Api::Config[:rate_limit_per_key] = 300 # Global limit per API key Spree::Api::Config[:rate_limit_window] = 60 # Window in seconds Spree::Api::Config[:rate_limit_login] = 5 # Login attempts per IP Spree::Api::Config[:rate_limit_register] = 3 # Registration attempts per IP Spree::Api::Config[:rate_limit_refresh] = 10 # Token refresh per IP Spree::Api::Config[:rate_limit_oauth] = 5 # OAuth callbacks per IP ``` Rate limiting uses `Rails.cache` as the backing store. For production environments with multiple application servers, ensure you're using a shared cache store like Redis: ```ruby theme={"theme":"night-owl"} # config/environments/production.rb config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] } ``` # Relations Source: https://spreecommerce.org/docs/api-reference/store-api/relations How to include related resources in API responses By default, the Store API returns only the primary resource attributes to keep responses fast and lightweight. Use the `expand` parameter to expand related resources inline. ## Including Relations Pass a comma-separated list of relation names via the `expand` query parameter: ```typescript SDK theme={"theme":"night-owl"} // Product with variants and media const product = await client.products.get('spree-tote', { expand: ['variants', 'media'], }) // Access included relations directly console.log(product.variants) // Array of variant objects console.log(product.media) // Array of media objects ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=variants,media' ``` ## Response Format Without `expand`, relation fields are omitted from the response: ```json theme={"theme":"night-owl"} { "id": "prod_86Rf07xd4z", "name": "Spree Tote", "slug": "spree-tote", "price": { "amount": "15.99", "currency": "USD" }, "created_at": "2025-01-15T10:30:00Z" } ``` With `?expand=variants,media`, the related resources are embedded: ```json theme={"theme":"night-owl"} { "id": "prod_86Rf07xd4z", "name": "Spree Tote", "slug": "spree-tote", "price": { "amount": "15.99", "currency": "USD" }, "created_at": "2025-01-15T10:30:00Z", "variants": [ { "id": "variant_k5nR8xLq", "sku": "SPR-TOTE-RED", "price": { "amount": "15.99", "currency": "USD" }, "in_stock": true, "option_values": [{ "name": "Red", "option_type_name": "Color" }] } ], "media": [ { "id": "asset_9xPq2wLm", "position": 1, "alt": "Spree Tote", "media_type": "image", "product_id": "prod_86Rf07xd4z", "variant_ids": ["variant_k5nR8xLq"], "original_url": "https://cdn.example.com/spree-tote.jpg", "small_url": "https://cdn.example.com/spree-tote-small.jpg", "medium_url": "https://cdn.example.com/spree-tote-medium.jpg", "large_url": "https://cdn.example.com/spree-tote-large.jpg" } ] } ``` ## Available Relations by Resource ### Products | Relation | Description | | ----------------- | --------------------------------------------------------- | | `variants` | All purchasable variants | | `default_variant` | The default variant | | `media` | All product media (images and videos across all variants) | | `primary_media` | The main product image | | `option_types` | Option types (e.g., Size, Color) | | `categories` | Categories this product belongs to | | `custom_fields` | Public custom fields (structured metadata) | | `prior_price` | Previous price history (for showing strikethrough) | ```typescript SDK theme={"theme":"night-owl"} const product = await client.products.get('spree-tote', { expand: ['variants', 'media', 'option_types', 'custom_fields'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=variants,media,option_types,custom_fields' ``` ### Categories | Relation | Description | | --------------- | ------------------------------------------ | | `parent` | Parent category | | `ancestors` | All ancestor categories (for breadcrumbs) | | `children` | Direct child categories | | `custom_fields` | Public custom fields (structured metadata) | ```typescript SDK theme={"theme":"night-owl"} const category = await client.categories.get('clothing/shirts', { expand: ['ancestors', 'children'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/categories/clothing/shirts' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=ancestors,children' ``` ### Countries | Relation | Description | | -------- | ----------------------------------- | | `states` | States/provinces within the country | ```typescript SDK theme={"theme":"night-owl"} const usa = await client.countries.get('US', { expand: ['states'], }) console.log(usa.states) // Array of state objects ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/countries/US' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=states' ``` ## Collections with Includes The `expand` parameter also works on collection endpoints: ```typescript SDK theme={"theme":"night-owl"} // List products with their media and default variant const { data: products } = await client.products.list({ expand: ['media', 'default_variant'], limit: 12, }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=media,default_variant' \ -d 'limit=12' ``` ## Nested Includes Use dot notation to include nested relations: ```typescript SDK theme={"theme":"night-owl"} // Include variants and their nested option values const product = await client.products.get('spree-tote', { expand: ['variants.option_values'], }) ``` ```bash cURL theme={"theme":"night-owl"} curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \ -H 'X-Spree-Api-Key: pk_xxx' \ -d 'expand=variants.option_values' ``` Only include relations you actually need. Each included relation requires additional database queries, so requesting unnecessary relations will slow down the response. # Add item to wishlist Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/add-item-to-wishlist /api-reference/store.yaml post /api/v3/store/wishlists/{wishlist_id}/items Adds a variant to the wishlist # Create a wishlist Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/create-a-wishlist /api-reference/store.yaml post /api/v3/store/wishlists Creates a new wishlist for the customer # Delete a wishlist Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/delete-a-wishlist /api-reference/store.yaml delete /api/v3/store/wishlists/{id} # Get a wishlist Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/get-a-wishlist /api-reference/store.yaml get /api/v3/store/wishlists/{id} # List wishlists Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/list-wishlists /api-reference/store.yaml get /api/v3/store/wishlists Returns all wishlists for the authenticated customer # Remove item from wishlist Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/remove-item-from-wishlist /api-reference/store.yaml delete /api/v3/store/wishlists/{wishlist_id}/items/{id} # Update a wishlist Source: https://spreecommerce.org/docs/api-reference/store-api/wishlists/update-a-wishlist /api-reference/store.yaml patch /api/v3/store/wishlists/{id} # Create an Address Source: https://spreecommerce.org/docs/api-reference/storefront/account-address/create-an-address /api-reference/storefront.yaml post /api/v2/storefront/account/addresses Creates a new address for the current user. # List all Addresses Source: https://spreecommerce.org/docs/api-reference/storefront/account-address/list-all-addresses /api-reference/storefront.yaml get /api/v2/storefront/account/addresses Returns a list of addresses for the current user. # Remove an Address Source: https://spreecommerce.org/docs/api-reference/storefront/account-address/remove-an-address /api-reference/storefront.yaml delete /api/v2/storefront/account/addresses/{id} This endpoint removes the specified address for the current user. It uses a soft delete to retain address information for pre-existing orders. # Update an Address Source: https://spreecommerce.org/docs/api-reference/storefront/account-address/update-an-address /api-reference/storefront.yaml patch /api/v2/storefront/account/addresses/{id} Updates the specified address for the current user. # List all Credit Cards Source: https://spreecommerce.org/docs/api-reference/storefront/account-credit-cards/list-all-credit-cards /api-reference/storefront.yaml get /api/v2/storefront/account/credit_cards Returns a list of credit cards for the current user. # Remove a Credit Card Source: https://spreecommerce.org/docs/api-reference/storefront/account-credit-cards/remove-a-credit-card /api-reference/storefront.yaml delete /api/v2/storefront/account/credit_cards/{id} Removes a specified credit card for the current user with a soft delete to retain payment information for any pre-existing orders. # Retrieve the default Credit Card Source: https://spreecommerce.org/docs/api-reference/storefront/account-credit-cards/retrieve-the-default-credit-card /api-reference/storefront.yaml get /api/v2/storefront/account/credit_cards/default Returns the current user's default credit card. # List all Orders Source: https://spreecommerce.org/docs/api-reference/storefront/account-orders/list-all-orders /api-reference/storefront.yaml get /api/v2/storefront/account/orders Returns all completed orders placed by the current user in the current store. # Retrieve an Order Source: https://spreecommerce.org/docs/api-reference/storefront/account-orders/retrieve-an-order /api-reference/storefront.yaml get /api/v2/storefront/account/orders/{order_number} Returns a completed order for the current user within the scope of the current store. # Create an Account Source: https://spreecommerce.org/docs/api-reference/storefront/account/create-an-account /api-reference/storefront.yaml post /api/v2/storefront/account Creates a new account # Retrieve an Account Source: https://spreecommerce.org/docs/api-reference/storefront/account/retrieve-an-account /api-reference/storefront.yaml get /api/v2/storefront/account Returns the current user details. # Update an Account Source: https://spreecommerce.org/docs/api-reference/storefront/account/update-an-account /api-reference/storefront.yaml patch /api/v2/storefront/account Updates the users account details # Complete an Adyen payment session and order Source: https://spreecommerce.org/docs/api-reference/storefront/adyen/complete-an-adyen-payment-session-and-order /api-reference/storefront.yaml post /api/v2/storefront/adyen/payment_sessions/{id}/complete Complete an Adyen payment session and order. Adyen support is currently in private beta. [Please contact us](https://spreecommerce.org/contact) to get access. # Create an Adyen Payment Session Source: https://spreecommerce.org/docs/api-reference/storefront/adyen/create-an-adyen-payment-session /api-reference/storefront.yaml post /api/v2/storefront/adyen/payment_sessions Create a new Adyen payment session with the specified amount. Adyen support is currently in private beta. [Please contact us](https://spreecommerce.org/contact) to get access. # Get Adyen Payment Session Source: https://spreecommerce.org/docs/api-reference/storefront/adyen/get-adyen-payment-session /api-reference/storefront.yaml get /api/v2/storefront/adyen/payment_sessions/{id} Retrieve An Adyen payment session information. Adyen support is currently in private beta. [Please contact us](https://spreecommerce.org/contact) to get access. # Authentication Source: https://spreecommerce.org/docs/api-reference/storefront/authentication Learn how to authenticate requests to the Storefront API. Storefront API requires authorization only for certain actions associated with user account (e.g. updating saved addresses) or manipulating cart and checkout. ## For guest users ### Using X-Spree-Order-Token header [Cart](/docs/api-reference/storefront/cart) and [Checkout](/docs/api-reference/storefront/checkout) endpoints paths also allow interactions without the bearer token to allow creating and managing guest checkouts. When you first create a cart via: ```bash theme={"theme":"night-owl"} curl --request POST \ --url 'https://demo.spreecommerce.org/api/v2/storefront/cart?fields%5Bcart%5D=number%2Ctoken' \ --header 'Content-Type: application/vnd.api+json' ``` You'll receive a response containing an empty cart. This response also contains a `data.token` attribute. ```json theme={"theme":"night-owl"} { "data": { "id": "114", "type": "cart", "attributes": { "number": "R522550303", "token": "NswjLVjZOw4OpEm1yWqlJg1713367317414" }, "relationships": {} } } ``` You can store this token in the frontend session (eg. Session Storage or a cookie) and pass it in a `X-Spree-Order-Token: {token}` header. ## For signed in users For users who have an account in your store, you will need to generate oAuth tokens to authenticate requests to endpoints such as [Account](/docs/api-reference/storefront/account), [Cart](/docs/api-reference/storefront/cart) and [Checkout](/docs/api-reference/storefront/checkout). ### Generating OAuth token To obtain a token, execute the following curl command: ```bash theme={"theme":"night-owl"} curl -X POST http://localhost:3000/spree_oauth/token \ --header "Content-Type: application/x-www-form-urlencoded" \ --data "grant_type=password&username=spree@example.com&password=spree123" ``` You should receive a JSON response with a `access_token` and a `refresh_token`, eg. ```json theme={"theme":"night-owl"} { "access_token": "CmVXoDp6f5Y51s2xFAAdSbdT5wcGZIuGKKr27zAkBvQ", "token_type": "Bearer", "expires_in": 7200, "refresh_token": "g5xPzY51_QYyok-ZcH9f4_btBkT_RZ6pB7ugGRaMjQQ", "created_at": 1713367848 } ``` ### Refreshing OAuth token OAuth tokens obtained via the previous step are valid only for a specific time (defined in `expires_in` attribute). After that period, you can refresh the token by executing the following curl command: ```bash theme={"theme":"night-owl"} curl -X POST http://localhost:3000/spree_oauth/token \ --header "Content-Type: application/x-www-form-urlencoded" \ --data "grant_type=refresh_token&refresh_token=g5xPzY51_QYyok-ZcH9f4_btBkT_RZ6pB7ugGRaMjQQ" ``` On a success, you'll receive a new bearer token to use when accessing the API, eg. ```json theme={"theme":"night-owl"} { "access_token": "JVMcaoCkxV4o2xofhAwf7ReeFEWUM94ZnVSKRITd3TQ", "token_type": "Bearer", "expires_in": 7200, "refresh_token": "nMlyfxfA9U6xfSDnFhoZQzL4IOkDc-EkW1y66ZTf5Oc", "created_at": 1713367950 } ``` # Apply a Coupon Code / Gift Card Source: https://spreecommerce.org/docs/api-reference/storefront/cart-coupons/apply-a-coupon-code-gift-card /api-reference/storefront.yaml patch /api/v2/storefront/cart/apply_coupon_code Applies a coupon code or gift card code to the current cart. # Remove a Coupon / Gift Card Source: https://spreecommerce.org/docs/api-reference/storefront/cart-coupons/remove-a-coupon-gift-card /api-reference/storefront.yaml delete /api/v2/storefront/cart/remove_coupon_code/{coupon_code} Removes a specified coupon code or gift card code from the current cart. # Remove all Coupons Source: https://spreecommerce.org/docs/api-reference/storefront/cart-coupons/remove-all-coupons /api-reference/storefront.yaml delete /api/v2/storefront/cart/remove_coupon_code Removes all coupons that have been applied to the current cart. # Add an Item to Cart Source: https://spreecommerce.org/docs/api-reference/storefront/cart-line-items/add-an-item-to-cart /api-reference/storefront.yaml post /api/v2/storefront/cart/add_item Adds a variant to the current cart by creating a line item. Each line item represents a specified variant and the desired quantity. # Remove a Line Item Source: https://spreecommerce.org/docs/api-reference/storefront/cart-line-items/remove-a-line-item /api-reference/storefront.yaml delete /api/v2/storefront/cart/remove_line_item/{id} Removes a line item from the cart by deleting the line item. After deleting the line item, the system will re-calculate taxes, promotions, shipments etc. # Set Line Item Quantity Source: https://spreecommerce.org/docs/api-reference/storefront/cart-line-items/set-line-item-quantity /api-reference/storefront.yaml patch /api/v2/storefront/cart/set_quantity Sets the quantity of a specified line item. # Associate a Cart with a User Source: https://spreecommerce.org/docs/api-reference/storefront/cart-other/associate-a-cart-with-a-user /api-reference/storefront.yaml patch /api/v2/storefront/cart/associate Associates a guest cart with the currently signed in user. # Change Cart Currency Source: https://spreecommerce.org/docs/api-reference/storefront/cart-other/change-cart-currency /api-reference/storefront.yaml patch /api/v2/storefront/cart/change_currency Changes the cart currency and recalculates the cart values. # Empty the Cart Source: https://spreecommerce.org/docs/api-reference/storefront/cart-other/empty-the-cart /api-reference/storefront.yaml patch /api/v2/storefront/cart/empty This endpoint removed all line items from the cart. Inventory that was previously taken by this order will be re-stocked. # List Estimated Shipping Rates Source: https://spreecommerce.org/docs/api-reference/storefront/cart-other/list-estimated-shipping-rates /api-reference/storefront.yaml get /api/v2/storefront/cart/estimate_shipping_rates Returns a list of shipping rates for the current cart. The rates given are only estimates and can vary from the final shipping rates. # Create a Cart Source: https://spreecommerce.org/docs/api-reference/storefront/cart/create-a-cart /api-reference/storefront.yaml post /api/v2/storefront/cart Creates a new cart. **Please be aware**; The `token` value found in the response body is used to authorize all future operations on this cart through the Auth API Key `X-Spree-Order-Token`. # Delete a Cart Source: https://spreecommerce.org/docs/api-reference/storefront/cart/delete-a-cart /api-reference/storefront.yaml delete /api/v2/storefront/cart Deletes a specified cart. When this endpoint is successfully executed, all shipments are cancelled, payments are voided, then line items, shipments & payments are all deleted. # Retrieve a Cart Source: https://spreecommerce.org/docs/api-reference/storefront/cart/retrieve-a-cart /api-reference/storefront.yaml get /api/v2/storefront/cart Returns the cart for the current user. # Create new Payment Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-payments/create-new-payment /api-reference/storefront.yaml post /api/v2/storefront/checkout/create_payment Creates new Payment for the current checkout. You can either create new payment source (eg. a Credit Card) or use an existing (for signed in users only). Newly created payment source will be associated to the current signed in user. System will automatically invalidate previous (non-finalized) payments (excluding store credit / gift card payments). [More details on payment system](/developer/core-concepts/payments) # List Payment Methods Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-payments/list-payment-methods /api-reference/storefront.yaml get /api/v2/storefront/checkout/payment_methods Returns a list of available payment methods for this checkout. # List Shipping Rates Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-shipments/list-shipping-rates /api-reference/storefront.yaml get /api/v2/storefront/checkout/shipping_rates Returns a list of available shipping rates for the checkout. Shipping rates are grouped against shipments. Each checkout can have multiple shipments. Some products are in stock and will be shipped immediately, while others might be back-ordered and sent later. # Selects shipping method for shipment(s) Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-shipments/selects-shipping-method-for-shipments /api-reference/storefront.yaml patch /api/v2/storefront/checkout/select_shipping_method Selects shipping method for Shipment(s). To generate shipments you first need to fetch shipping rates via [Get Shipping Rates](/api-reference/storefront/checkout-shipments/list-shipping-rates) endpoint # Advance Checkout Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-state/advance-checkout /api-reference/storefront.yaml patch /api/v2/storefront/checkout/advance Advances the checkout to the furthest checkout step validation allows, up until the **completed** step. # Complete Checkout Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-state/complete-checkout /api-reference/storefront.yaml patch /api/v2/storefront/checkout/complete Completes the checkout by marking the order as complete. **Please be aware**; Once a checkout is marked as `complete` it is considered a completed order, the Cart and Checkout endpoints will no longer access a completed order, and a new cart will need to be created. If you want to access a completed order you will need to use the **Account / Orders** or **Order Status** endpoints. # Next Checkout Step Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-state/next-checkout-step /api-reference/storefront.yaml patch /api/v2/storefront/checkout/next Goes to the next checkout step. # Add Store Credit Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-store-credit/add-store-credit /api-reference/storefront.yaml post /api/v2/storefront/checkout/add_store_credit The Add Store Credit endpoint takes the users existing store credit from their account and adds a set amount of this store credit to the current cart. In essence, by adding the user's store credit to a cart, the user's store credit is being spent. # Remove Store Credit Source: https://spreecommerce.org/docs/api-reference/storefront/checkout-store-credit/remove-store-credit /api-reference/storefront.yaml post /api/v2/storefront/checkout/remove_store_credit Removes store credit from the cart if any had previously been applied. # Update Checkout Source: https://spreecommerce.org/docs/api-reference/storefront/checkout/update-checkout /api-reference/storefront.yaml patch /api/v2/storefront/checkout The Update Checkout endpoint allows you to manage the typical stages of an e-commerce checkout system. # Validate order payment Source: https://spreecommerce.org/docs/api-reference/storefront/checkout/validate-order-payment /api-reference/storefront.yaml post /api/v2/storefront/checkout/validate_order_for_payment Validate an order before placing it. It will respond with an error when: - cart was changed - any order item is out of stock - any order item is discontinued # Get Default Country Source: https://spreecommerce.org/docs/api-reference/storefront/countries/get-default-country /api-reference/storefront.yaml get /api/v2/storefront/countries/default Returns the default country for the current store. By default this will be the US. # List all Countries Source: https://spreecommerce.org/docs/api-reference/storefront/countries/list-all-countries /api-reference/storefront.yaml get /api/v2/storefront/countries Returns a list of all countries. # Retrieve a Country Source: https://spreecommerce.org/docs/api-reference/storefront/countries/retrieve-a-country /api-reference/storefront.yaml get /api/v2/storefront/countries/{iso} Returns the details of a specific country. # Download a Digital Asset Source: https://spreecommerce.org/docs/api-reference/storefront/digital-downloads/download-a-digital-asset /api-reference/storefront.yaml get /api/v2/storefront/digitals/{token} This endpoint allows you to download a digital item using the unique digital_link token. The digital_link token value can be found by including `line_items.digital_links` in a completed order lookup. Use the **Retrieve an Order Status** endpoint found under the Order Status tab, or the **Retrieve an Order** endpoint found under the Account / Orders tab to perform a completed order lookup. Depending on the store that the item was purchased in, downloads may be restricted to a set number of days since the purchase was made. Additionally, the number of times a download link can be used may be restricted. If you are not authorized to perform the download due to the download limit restrictions mentioned above, a 403 response will be returned. # Retrieve an Order Status Source: https://spreecommerce.org/docs/api-reference/storefront/order-status/retrieve-an-order-status /api-reference/storefront.yaml get /api/v2/storefront/order_status/{order_number} Returns completed order information. This endpoint is useful when fetching orders placed by a guest user, allowing customers without an account to check the status of their order: (shipment/payment/processing) etc. Pass the cart `token` value as the `X-Spree-Order-Token` for authorization, and the corresponding cart `number` in the URI **{order_number}** to successfully retrieve an order. # List all Store Policies Source: https://spreecommerce.org/docs/api-reference/storefront/policies/list-all-store-policies /api-reference/storefront.yaml get /api/v2/storefront/policies Returns a list of Store Policies. This endpoint is only available in Spree 5.2 or later. # Retrieve a Policy Source: https://spreecommerce.org/docs/api-reference/storefront/policies/retrieve-a-policy /api-reference/storefront.yaml get /api/v2/storefront/policies/{policy_slug} Returns the details of a specified Policy. This endpoint is only available in Spree 5.2 or later. # List all Post Categories Source: https://spreecommerce.org/docs/api-reference/storefront/post-categories/list-all-post-categories /api-reference/storefront.yaml get /api/v2/storefront/post_categories Returns a list of Post Categories. This endpoint is only available in Spree 5.2 or later. # Retrieve a Post Category Source: https://spreecommerce.org/docs/api-reference/storefront/post-categories/retrieve-a-post-category /api-reference/storefront.yaml get /api/v2/storefront/post_categories/{id} Returns the details of a specified Post Category, including related posts. You can use either the category slug or ID. This endpoint is only available in Spree 5.2 or later. # List all Posts Source: https://spreecommerce.org/docs/api-reference/storefront/posts/list-all-posts /api-reference/storefront.yaml get /api/v2/storefront/posts Returns a list of published Posts. This endpoint is only available in Spree 5.2 or later. # Retrieve a Post Source: https://spreecommerce.org/docs/api-reference/storefront/posts/retrieve-a-post /api-reference/storefront.yaml get /api/v2/storefront/posts/{id} Returns the details of a specified Post. You can use either the post slug or ID. This endpoint is only available in Spree 5.2 or later. # List all Products Source: https://spreecommerce.org/docs/api-reference/storefront/products/list-all-products /api-reference/storefront.yaml get /api/v2/storefront/products Returns a list of products for the current Store. # Retrieve a Product Source: https://spreecommerce.org/docs/api-reference/storefront/products/retrieve-a-product /api-reference/storefront.yaml get /api/v2/storefront/products/{product_slug} Returns Product details. You can use product permalink: ``` GET /api/v2/storefront/products/knitted-high-neck-sweater ``` Or Product ID: ``` GET /api/v2/storefront/products/21 ``` **Note** API will attempt a permalink lookup before an ID lookup. # Return the current Store Source: https://spreecommerce.org/docs/api-reference/storefront/stores/return-the-current-store /api-reference/storefront.yaml get /api/v2/storefront/store Returns the current Store. [Read more about Stores](/developer/core-concepts/stores) # Create a Stripe Payment Intent Source: https://spreecommerce.org/docs/api-reference/storefront/stripe/create-a-stripe-payment-intent /api-reference/storefront.yaml post /api/v2/storefront/stripe/payment_intents Create a Stripe Payment Intent using provided payment method. It creates a Stripe customer if not exists. # Create a Stripe Setup Intent Source: https://spreecommerce.org/docs/api-reference/storefront/stripe/create-a-stripe-setup-intent /api-reference/storefront.yaml post /api/v2/storefront/stripe/setup_intents First, this endpoint creates a customer in Stripe (if it doesn't already exist), then it creates an Ephemeral Key for the customer, and finally, it creates a Setup Intent for the customer. # Mark the payment intent as confirmed, and move the order to the complete state Source: https://spreecommerce.org/docs/api-reference/storefront/stripe/mark-the-payment-intent-as-confirmed-and-move-the-order-to-the-complete-state /api-reference/storefront.yaml patch /api/v2/storefront/stripe/payment_intents/{id}/confirm This endpoint is used to complete the order with succeeded payment intent. First it will check if the payment intent has `status` `succeeded`, if yes, then it will move the order to the complete state. # Return a Stripe Payment Intent Source: https://spreecommerce.org/docs/api-reference/storefront/stripe/return-a-stripe-payment-intent /api-reference/storefront.yaml get /api/v2/storefront/stripe/payment_intents/{id} Returns the Payment Intent information # Updates Stripe Payment Intent Source: https://spreecommerce.org/docs/api-reference/storefront/stripe/updates-stripe-payment-intent /api-reference/storefront.yaml patch /api/v2/storefront/stripe/payment_intents/{id} Updates the payment intent with new `amount` and `stripe_payment_method_id` # List all Taxons Source: https://spreecommerce.org/docs/api-reference/storefront/taxons/list-all-taxons /api-reference/storefront.yaml get /api/v2/storefront/taxons Returns a list of Taxons. [Read more about Taxons](/developer/core-concepts/products#taxons-and-taxonomies) # Retrieve a Taxon Source: https://spreecommerce.org/docs/api-reference/storefront/taxons/retrieve-a-taxon /api-reference/storefront.yaml get /api/v2/storefront/taxons/{taxon_permalink} Returns the details of a specified taxon. # List all Product Variants Source: https://spreecommerce.org/docs/api-reference/storefront/variants/list-all-product-variants /api-reference/storefront.yaml get /api/v2/storefront/products/{product_slug}/variants Returns a list of product variants. You can use product permalink: ``` GET /api/v2/storefront/products/knitted-high-neck-sweater/variants ``` Or Product ID: ``` GET /api/v2/storefront/products/21/variants ``` **Note** API will attempt a permalink lookup before an ID lookup. # List all Vendors Source: https://spreecommerce.org/docs/api-reference/storefront/vendors/list-all-vendors /api-reference/storefront.yaml get /api/v2/storefront/vendors Returns a list of Vendors. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Retrieve a Vendor Source: https://spreecommerce.org/docs/api-reference/storefront/vendors/retrieve-a-vendor /api-reference/storefront.yaml get /api/v2/storefront/vendors/{vendor_slug} Returns the details of a specified Vendor. Only available in [Enterprise Edition](https://spreecommerce.org/pricing) # Add Item to Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists-wished-items/add-item-to-wishlist /api-reference/storefront.yaml post /api/v2/storefront/wishlists/{token}/add_item The Add Item to Wishlist endpoint adds a variant to an existing wishlist by creating a new `wished_item`. A wished item in a wishlist is comparable to a line item in a cart. Each wished item references a single variant, holds the desired quantity and returns price information determined by the variant price in current currency, and desired quantity. # Add Items to Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists-wished-items/add-items-to-wishlist /api-reference/storefront.yaml post /api/v2/storefront/wishlists/{token}/add_items The Add Items to Wishlist endpoint adds variants to an existing wishlist by creating a new `wished_item` objects. A wished item in a wishlist is comparable to a line item in a cart. Each wished item references a single variant, holds the desired quantity and returns price information determined by the variant price in current currency, and desired quantity. # Delete Item from Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists-wished-items/delete-item-from-wishlist /api-reference/storefront.yaml delete /api/v2/storefront/wishlists/{token}/remove_item/{item_id} Removes a wished item from a wishlist. # Delete Items from Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists-wished-items/delete-items-from-wishlist /api-reference/storefront.yaml delete /api/v2/storefront/wishlists/{token}/remove_items Removes wished items from a wishlist. # Set Wished Item Quantity Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists-wished-items/set-wished-item-quantity /api-reference/storefront.yaml patch /api/v2/storefront/wishlists/{token}/set_item_quantity/{item_id} This endpoint sets the wished item quantity. # Create a Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists/create-a-wishlist /api-reference/storefront.yaml post /api/v2/storefront/wishlists Creates a new wishlist for the current user in the current store. # Delete a Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists/delete-a-wishlist /api-reference/storefront.yaml delete /api/v2/storefront/wishlists/{token} This operation deletes the wishlist identified in the URI `token`. # List all Wishlists Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists/list-all-wishlists /api-reference/storefront.yaml get /api/v2/storefront/wishlists Returns all wishlists available to the current user, in the current store. # Retrieve a Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists/retrieve-a-wishlist /api-reference/storefront.yaml get /api/v2/storefront/wishlists/{token} Retrieves a wishlist using the wishlist token. If the wishlist is publicly viewable, the endpoint will return the requested wishlist regardless of the user. If the wishlist is private, only the wishlist owner can access the wishlist. # Retrieve the default Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists/retrieve-the-default-wishlist /api-reference/storefront.yaml get /api/v2/storefront/wishlists/default Returns the default wishlist for the current user, in the current store. If the user does not have a default wishlist in the current store one will be created. # Update a Wishlist Source: https://spreecommerce.org/docs/api-reference/storefront/wishlists/update-a-wishlist /api-reference/storefront.yaml patch /api/v2/storefront/wishlists/{token} Updates the specific Wishlist by setting the values passed in the request body. Any parameters not provided will be left unchanged. # Adyen Integration Guide for Android Source: https://spreecommerce.org/docs/api-reference/tutorials/adyen-integration-guide-for-android This guide provides step-by-step instructions for integrating Adyen Android Drop-in with spree_adyen using session flow and Drop-In component. This guide is aimed at advanced users who want to create adyen integration for their custom android application. You also need to install Spree Adyen extension before. ## Note The list of available library versions can be found on [Official Adyen Documentation for Android integration](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Android\&integration=Drop-in\&version=5.13.1) Current newest version available at the moment of writing the tutorial is 5.13.1. This doc will be referring to the library version as YOUR\_VERSION. ## Resources * [Official Adyen Documentation for Android integration](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Android\&integration=Drop-in\&version=5.13.1) * Spree Adyen API docs - [create payment\_session](/docs/api-reference/storefront/adyen/create-an-adyen-payment-session) and [get payment\_session](/docs/api-reference/storefront/adyen/get-adyen-payment-session) * [Official Adyen example for Android integration](https://github.com/adyen-examples/adyen-android-online-payments) ## Overview The integration consists of two main components: * **Spree Adyen**: Provides API for your client and receive payment result from Adyen * **Your Android client app**: Shows the Drop-in UI and handles payment flow ## Step 1: Import the library Import the compatibility module: ### Import the module with Jet Compose ```bash theme={"theme":"night-owl"} implementation "com.adyen.checkout:drop-in-compose:YOUR_VERSION" ``` ### Import without Jet Compose ```bash theme={"theme":"night-owl"} implementation "com.adyen.checkout:drop-in:YOUR_VERSION" ``` ## Step 2: Create a checkout session Create session using [this endpoint](/docs/api-reference/storefront/adyen/create-an-adyen-payment-session). ```kotlin theme={"theme":"night-owl"} val sessionModel = SessionModel.SERIALIZER.deserialize(sessionsResponseJSON) ``` sessionsResponseJSON should contain: * `sessionData` - available as `adyen_data` in payment\_session API response * `id` - available as `adyen_id` in payment\_session API response ```kotlin theme={"theme":"night-owl"} val sessionModel = SessionModel.SERIALIZER.deserialize(sessionsResponseJSON) ``` ## Step 3 call `CheckoutSessionProvider.createSession` passing serialized session data (`sessionModel`) and `dropInConfiguration` dropInConfiguration example: ```kotlin theme={"theme":"night-owl"} val checkoutConfiguration = CheckoutConfiguration( environment = environment, clientKey = clientKey, shopperLocale = shopperLocale, // Optional ) { // Optional: add Drop-in configuration. dropIn { setEnableRemovingStoredPaymentMethods(true) } // Optional: add or change default configuration for the card payment method. card { setHolderNameRequired(true) setShopperReference("...") } // Optional: change configuration for 3D Secure 2. adyen3DS2 { setThreeDSRequestorAppURL("...") } } ``` see also: [https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Web\&integration=Drop-in\&version=6.18.1#create-instance](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Web\&integration=Drop-in\&version=6.18.1#create-instance) `environment` - enum: `live` or `test` `clientKey` - `client_key` from payment\_sessions endpoint `shopperLocale` - shopper locale in ISO format ```kotlin theme={"theme":"night-owl"} // Create an object for the checkout session. val result = CheckoutSessionProvider.createSession(sessionModel, dropInConfiguration) // If the payment session is successful, handle the result. // If the payment session encounters an error, handle the error. when (result) { is CheckoutSessionResult.Success -> handleCheckoutSession(result.checkoutSession) is CheckoutSessionResult.Error -> handleError(result.exception) } ``` ## Step 4: Launch and show Drop-in ```kotlin theme={"theme":"night-owl"} override fun onDropInResult(sessionDropInResult: SessionDropInResult?) { when (sessionDropInResult) { // The payment finishes with a result. is SessionDropInResult.Finished -> handleResult(sessionDropInResult.result) // The shopper dismisses Drop-in. is SessionDropInResult.CancelledByUser -> // Drop-in encounters an error. is SessionDropInResult.Error -> handleError(sessionDropInResult.reason) // Drop-in encounters an unexpected state. null -> } } ``` ## Step 5: Create the Drop-in launcher DropIn.startPayment, passing: * `dropInLauncher` - The Drop-in launcher object * `checkoutSession` - result of `CheckoutSessionProvider.createSession` * `dropInConfiguration` - Your Drop-in configuration Example: ```kotlin theme={"theme":"night-owl"} import com.adyen.checkout.dropin.compose.startPayment import com.adyen.checkout.dropin.compose.rememberLauncherForDropInResult @Composable private fun ComposableDropIn() { val dropInLauncher = rememberLauncherForDropInResult(sessionDropInCallback) DropIn.startPayment(dropInLauncher, checkoutSession, dropInConfiguration) } ``` ## Get payment outcome 1. Wait for backend to process the payment 2. Continue shopping experience ### 1. Wait for backend to process the payment backend will change the state of `payment_session` to one of the following state * `pending` - chosen payment method can take a while to complete * `completed` - payment resulted in success, order completed * `canceled` - payment canceled, payment is `void` * `refused` - payment failed ### 2. Continue shopping experience if succeed - order is processed and completed if failed - payment can be retried using new payment session # Adyen Integration Guide for iOS Source: https://spreecommerce.org/docs/api-reference/tutorials/adyen-integration-guide-for-ios This guide provides step-by-step instructions for integrating Adyen iOS Drop-in with spree_adyen using session flow and Drop-In component. This guide is aimed at advanced users who want to create adyen integration for their custom iOS application. You also need to install Spree Adyen extension before. ## Requirements * Adyen test account * Set up `spree_adyen` spree extension ### return\_url spree\_adyen needs to be configured with `return_url`. Return url tells where shopper should be redirect after the payment from outside your application (for example klarna or most of others buy now pay later systems). Use the custom URL for your app, like `my-app://adyen`. Url can contain custom query params however do not include any personally identifiable information (PII) of your customer. Maximum length of the url is 1024 characters. ## Note The list of available library versions can be found on [Official Adyen Documentation for iOS integration](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=iOS\&integration=Drop-in\&version=5.13.1) Current newest version available at the moment of writing the tutorial is 5.19.2. ## Resources * [Official Adyen Documentation for iOS integration](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=iOS\&integration=Drop-in\&version=5.19.2) * Spree Adyen API docs - [create payment\_session](/docs/api-reference/storefront/adyen/create-an-adyen-payment-session) and [get payment\_session](/docs/api-reference/storefront/adyen/get-adyen-payment-session) * [Official Adyen example for iOS integration](https://github.com/Adyen/adyen-ios/tree/develop/Demo) * Apple Developer documentation on [defining custom url scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app) ## Overview The integration consists of two main components: * **Spree Adyen**: Provides API for your client and receive payment result from Adyen * **Your iOS client app**: Shows the Drop-in UI and handles payment flow ## Step 1: Install adyen with Swift Package Manager repository URL: ``` https://github.com/Adyen/adyen-ios ``` use at least version 5.0.0 CocoaPods and Carthage instructions are available [here](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=iOS\&integration=Drop-in\&version=5.19.2\&tab=cocoapods_1_2#1-get-adyen-ios). ## Step 2: Create the context Create the instance of APIContext that includes client key and environment setting. ```swift theme={"theme":"night-owl"} // Set the client key and environment in an instance of APIContext. let apiContext = APIContext(clientKey: clientKey, environment: Environment.test) // Set the environment to a live one when going live. // Create the amount with the value in minor units and the currency code. let amount = Amount(value: 1000, currencyCode: "EUR") // Create the payment object with the amount and country code. let payment = Payment(amount: amount, countryCode: "NL") // Create an instance of AdyenContext, passing the instance of APIContext, and payment object. let adyenContext = AdyenContext(apiContext: apiContext, payment:payment) ``` `environment` - Environment.test or Environment.live `clientKey` - `client_key` from payment\_sessions endpoint ## Step 3: Create and set up session Create a session using [payment\_session endpoint](/docs/api-reference/storefront/adyen/create-an-adyen-payment-session). Then set a configuration using data from the response: ```swift theme={"theme":"night-owl"} let configuration = AdyenSession.Configuration(sessionIdentifier: sessionId, initialSessionData: data) ``` * `data` - available as `adyen_data` in payment\_session API response * `sessionId` - available as `adyen_id` in payment\_session API response With the configuration you initialize AdyenSession: ```swift theme={"theme":"night-owl"} AdyenSession.initialize(with: configuration, delegate: self, presentationDelegate: self) { [weak self] result in switch result { case let .success(session): //Store the session object. self?.session = session case let .failure(error): //Handle the error. } } ``` ## Step 4. Configure Drop-in ```swift theme={"theme":"night-owl"} let dropInConfiguration = DropInComponent.Configuration() // Some payment methods have additional required or optional configuration. // For example, an optional configuration to show the cardholder name field for cards. dropInConfiguration.card.showsHolderNameField = true ``` ## Step 5: Initialize the DropInComponent class ```swift theme={"theme":"night-owl"} let dropInComponent = DropInComponent(paymentMethods: session.sessionContext.paymentMethods, context: adyenContext, configuration: dropInConfiguration) // Keep the instance of Drop-in to so that it doesn't get destroyed after the function is executed. self.dropInComponent = dropInComponent // Set the session as the delegate. dropInComponent.delegate = session // If you support gift cards, set the session as the partial payment delegate. dropInComponent.partialPaymentDelegate = session ``` ## Step 6: Show Drop-in in your app ```swift theme={"theme":"night-owl"} myCheckoutViewController.present(dropInComponent.viewController, animated: true) ``` If the `action` field is `redirect` you need to handle the redirect result. ## Step 7. Handling the redirect result If the `action` field returns `redirect` the shopper is completing the payment outside of your application. You need to inform the Drop-in when the shopper returns to your app. Here an example for a Custom Url scheme: * implement the following in your `UIApplicationDelegate`: ```swift theme={"theme":"night-owl"} func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { return RedirectComponent.applicationDidOpen(from: url) } ``` for Universal URL use this instead: ```swift theme={"theme":"night-owl"} func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let incomingURL = userActivity.webpageURL else { return false } return RedirectComponent.applicationDidOpen(from: incomingURL) } ``` ## Step 8: Show the shopper result of the payment When the payment flow is finished, your instance of AdyenSession calls the didComplete method. Implement the following in your Drop-in configuration object. ```swift theme={"theme":"night-owl"} func didComplete(with result: AdyenSessionResult, // The session result. component: Component, // The Drop-in component. session: AdyenSession) // Your instance of AdyenSession. ``` Use the resultCode to inform your shopper about the current payment status. Possible resultCode values: * `authorised` - payment authorised * `refused` - payment refused * `pending` - payment pending (for payments with asynchronous flow like iDEAL). When the shopper completes the payment webhook will process the payment. * `cancelled` - payment canceled * `received` - some payments needs more time to be processed. When the status is available webhook will process the payment * `error` - an error occurred when processing the payment. The response contains more details with the error code Your instance of AdyenSession calls the didFail method containing the error. ```swift theme={"theme":"night-owl"} func didFail(with error: Error, // The error object. from component: Component, // The Drop-in component. session: AdyenSession) // Your instance of AdyenSession. ``` ## Step 8: Continue shopping experience 1. Wait for backend to process the payment 2. Continue shopping experience ### 1. Wait for backend to process the payment backend after receiving webhook will change the state of `payment_session` to one of the following state: * `pending` - chosen payment method can take a while to complete * `completed` - payment resulted in success, order completed * `canceled` - payment canceled, payment is `void` * `refused` - payment failed ### 2. Continue shopping experience if succeed - order is processed and completed if failed - payment can be retried using new payment session # Quick Checkout with Stripe and Storefront API Source: https://spreecommerce.org/docs/api-reference/tutorials/quick-checkout-with-stripe This tutorial will show you how to add quick checkout to your headless/composable storefront using Stripe and Spree APIs. This guide is aimed at advanced users who have some experience with Spree API and Stripe. You also need to install [Spree Stripe](https://github.com/spree/spree_stripe) extension before. If you're using the standard Spree Storefront, then you can skip this tutorial as quick checkout is already built in. ## Prerequisites Assume that you have a cart made with line items in it already. Quick checkout works by handling 3 different events: * Address change * Shipping Method/Rate change * Confirmation How you should handle them depends on your platform/language: * For Web and JS, please review: * [Express Checkout Element](https://docs.stripe.com/elements/express-checkout-element) * We also recommend reviewing the [spree\_stripe implementation in Stimulus.js](https://github.com/spree/spree_stripe/blob/main/app/javascript/spree_stripe/controllers/stripe_button_controller.js) * For iOS, please review: * [StripeApplePay](https://docs.stripe.com/apple-pay) * [STPApplePayContext](https://stripe.dev/stripe-ios/stripeapplepay/documentation/stripeapplepay/stpapplepaycontext#instance-methods) ## Checkout Flow ### Address change Here we need to send some basic information about the customer's shipping address, and we should move the order to the delivery step (don't forget to replace the placeholders with the customer's data): ```bash Update order [expandable] theme={"theme":"night-owl"} curl -X PATCH /api/v2/storefront/checkout \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "order": { "ship_address_attributes": { "quick_checkout": true, "firstname": , "lastname": , "city": , "zipcode": , "country_iso": , "state_name": }, "bill_address_id": "CLEAR" } }' ``` `true` indicating that the order is from quick checkout. It is required as the address details you will receive from Apple Pay or Google Pay are not complete and wouldn't be valid for the standard checkout. Customer's first name, e.g. John Customer's last name, e.g. Doe Customer's city, e.g. Mountain View Customer's zip code, e.g. 94043 Customer's country ISO, e.g. US Customer's address state, e.g. CA (empty string is also accepted) Set it to `CLEAR` to clear the existing billing address (this prevents the order from accidentally advancing to the `payment` step). [Full documentation](/docs/api-reference/storefront/checkout/update-checkout) [Endpoint documentation](/docs/api-reference/storefront/checkout-state/advance-checkout) ```bash Advance order to the delivery step theme={"theme":"night-owl"} curl -X PATCH '/api/v2/storefront/checkout/advance?state=delivery&include=shipments.shipping_rates' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "quick_checkout": true, "shipping_method_id": }' ``` `true` indicating that the order is from quick checkout You don't have to send `shipping_method_id` if you don't have it yet, but if you received `Shipping Method/Rate changed` event, and you already have the selected shipping method, then you can send it. This will return you an order response, and you can update the payment element with a new total (you should use `total_minus_store_credits` for total) and shipping rates. ### Shipping Method/Rate change Here we need to send the selected shipping method to the backend and update the total in the payment element [Endpoint documentation](/docs/api-reference/storefront/checkout-shipments/selects-shipping-method-for-shipments) ```bash Select shipping method theme={"theme":"night-owl"} curl -X PATCH '/api/v2/storefront/checkout/select_shipping_method' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "shipping_method_id": }' ``` Shipping method ID of the selected shipping rate Subsequently, update the payment element with a new total taken from the response's `total_minus_store_credits` ### Confirming the payment Now you should make several more calls to the backend ```bash Validate order for payment theme={"theme":"night-owl"} curl -X POST '/api/v2/storefront/checkout/validate_order_for_payment?skip_state=true' \ -H 'Authorization: Bearer ' ``` `200` response code means that the order is ready for the payment. [Endpoint documentation](/docs/api-reference/storefront/checkout/validate-order-payment) You should have a lot more information about the customer (in previous events, Stripe probably will not provide you information such as customer address), so send them to the backend: [Endpoint documentation](/docs/api-reference/storefront/checkout/update-checkout) ```bash Update the order [expandable] theme={"theme":"night-owl"} curl -X PATCH /api/v2/storefront/checkout \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "order": { "email": , "ship_address_attributes": { "quick_checkout": true, "firstname": , "lastname": , "address1":
, "address2":
, "city": , "zipcode": , "country_iso": , "state_name": , "phone": } }, "do_not_change_state": true }' ``` Customer's email, e.g. [email@example.com](mailto:email@example.com) `true` indicating that the order is from quick checkout Customer's first name, e.g. John Customer's last name, e.g. Doe Customer's address, e.g. 123 Main St Customer's address 2, e.g., Apt. 1 Customer's zip code, e.g. 94043 Customer's country ISO, e.g. US Customer's address state, e.g. CA (empty string is also accepted) Customer's phone number, e.g. +1234567890 Set it to `true` so the order does not advance to the next state [Endpoint documentation](/docs/api-reference/storefront/checkout-state/advance-checkout) ```bash Move the order to the payment step theme={"theme":"night-owl"} curl -X PATCH '/api/v2/storefront/checkout/advance?state=payment' \ -H 'Authorization: Bearer ' \ -d '{ "shipping_method_id": }' ``` Shipping method ID of the selected shipping rate Thereafter, you should confirm the payment intent using the Stripe library; this depends on your platform/language. `` should be Spree's internal payment intent ID, which you will receive when you create the payment intent. Do not confuse it with Stripe's payment intent ID. If you like to redirect the user to Spree's order summary page, then set the `return_url` when confirming the payment intent to `/stripe/payment_intents/`. This will check if the payment intent is confirmed, move the order to the complete state, and redirect the user to the order summary. If you like to make the order summary page by yourself, then after confirming the payment intent in Stripe, make this request: ```bash Confirm payment intent theme={"theme":"night-owl"} curl -X POST /api/v2/storefront/stripe/payment_intents//confirm \ -H 'Authorization: Bearer ' ``` [Endpoint documentation](/docs/api-reference/storefront/stripe/mark-the-payment-intent-as-confirmed-and-move-the-order-to-the-complete-state) This will also check if the payment intent is confirmed, and it will move the order to the complete state ### User cancels the payment If at any point, the user cancels the payment (closes the quick checkout sheet/modal), you will need to handle this event and clear the shipping and billing address from the order as these addresses will not be valid and if someones decides to use standard checkout it could lead to errors. ```bash Reset order addresses theme={"theme":"night-owl"} curl -X PATCH /api/v2/storefront/checkout \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "order": { "ship_address_id": "CLEAR", "bill_address_id": "CLEAR" } }' ``` [Endpoint documentation](/docs/api-reference/storefront/checkout/update-checkout) # Fetching multiple resources Source: https://spreecommerce.org/docs/api-reference/v2/fetching-multiple-resources Spree APIs allow you to fetch multiple associated resources in one API call, both for singular resources and collections, eg. we can fetch a Product with all of its variants and images: ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products/classic-varsity-top?include=variants%252Cimages' \ --header 'Accept: application/vnd.api+json' ``` This will return a JSON response with `data[relationships]` node (links to what resources connects to which) and `included` node (images and variants JSON responses). This way you can easily control what resources you want to include in the response and avoid making multiple API calls. ## Nested resources You can also nest resources, eg. fetching Product Variants with their Option Values: ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products/classic-varsity-top?include=variants.option_values' \ --header 'Accept: application/vnd.api+json' ``` ## Open source libraries to transform JSONApi to a flat JSON response As you probably noticed the JSON response can sometimes be quite big and hard to follow. To ease working with included resources we recommend checking out [one of the open-source libraries](https://jsonapi.org/implementations/) that can help deserialize responses. # Filtering and sorting Source: https://spreecommerce.org/docs/api-reference/v2/filtering-and-sorting ## Filtering You can filter out collection results by applying filter parameters to your API calls, eg. ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products' \ --header 'Accept: application/vnd.api+json' \ --data-urlencode 'filter[skus]=classic-varsity-top-large' ``` You can combine multiple filters to find only records that match both criteria: ```http theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products' \ --header 'Accept: application/vnd.api+json' \ --data-urlencode 'filter[price]=10,100' \ --data-urlencode 'filter[name]=Classic Varsity' ``` [Products list endpoint ](https://developers.spreecommerce.org/reference/products-list) covers how to filter by these attributes in more detail. ## Sorting Sorting is being performed by supplying `sort` parameter in your API calls. Ascending sort, eg. ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products?sort=price' \ --header 'Accept: application/vnd.api+json' ``` Descending sort is performed by adding `-` before the column name, eg: ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products?sort=-price' \ --header 'Accept: application/vnd.api+json' ``` You can also combine multiple attributes: ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products' \ --data-urlencode 'sort=-price,updated_at' \ --header 'Accept: application/vnd.api+json' ``` Each endpoint includes a list of attributes you can sort by. The default ones are always `id`, `created_at`, `updated_at` and `name`. # Ecommerce API Source: https://spreecommerce.org/docs/api-reference/v2/introduction Spree open-source comes with a fully-featured Ecommerce API for headless eCommerce apps. Spree Commerce open-source comes with a fully-featured Ecommerce API enabling a fully headless mode. This means that you can use Spree as a backend to build your custom [Next.js storefront](https://spreecommerce.org/category/next-js-ecommerce/), eCommerce mobile app functionality, or any other customer-facing application that needs to interact with an e-commerce platform. ## API v2 Currently Spree includes 2 modern REST APIs: Designed to allow customers to interact with the store via external applications (e.g. Next.js storefront or a dedicated mobile app) Provides management capabilities, allowing third party apps to perform actions otherwise available via the admin panel. ## Thank you! Give Spree a [GitHub Star](https://github.com/spree/spree), why don't ya? Thank you for supporting Spree open-source! Need support or want to give some feedback? Join our [Discord](https://discord.spreecommerce.org/) # Pagination Source: https://spreecommerce.org/docs/api-reference/v2/pagination All lists implement the same method for pagination with `page` and `per_page` parameters, eg. ```curl theme={"theme":"night-owl"} curl --request GET \ --url 'https://demo.spreecommerce.org/api/v2/storefront/products?page=1&per_page=25' \ --header 'Accept: application/vnd.api+json' ``` Both of these parameters are optional. There is a limit of 500 records that can be fetched by a single request. If this doesn't work for your use case, please contact us so we can work on resolving this issue. Response for that call will include a `meta` node with: ```json theme={"theme":"night-owl"} "meta": { "count": 25, "total_count": 26, "total_pages": 2 } ``` `count` is the number of records returned on the current page\ `total_count` is the total number of records in the collection\ `total_pages` is the total number of pages The response will also include the`links` node: ```json theme={"theme":"night-owl"} "links": { "self": "https://demo.spreecommerce.org/api/v2/storefront/products?per_page=25", "next": "https://demo.spreecommerce.org/api/v2/storefront/products?page=2&per_page=25", "prev": "https://demo.spreecommerce.org/api/v2/storefront/products?page=1&per_page=25", "last": "https://demo.spreecommerce.org/api/v2/storefront/products?page=2&per_page=25", "first": "https://demo.spreecommerce.org/api/v2/storefront/products?page=1&per_page=25" } ``` With fully generates API URLs for pagination. # Webhook Events & Payloads Source: https://spreecommerce.org/docs/api-reference/webhooks-events Complete reference of all webhook event types and their payload schemas Every webhook delivery sends a JSON envelope with the event metadata and a `data` object containing the serialized resource: ```json theme={"theme":"night-owl"} { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "order.completed", "created_at": "2025-01-15T10:30:00Z", "data": { ... }, "metadata": { "spree_version": "5.4.0" } } ``` | Field | Type | Description | | ------------ | ------ | ------------------------------------------ | | `id` | string | Unique UUID for this event | | `name` | string | Event name (e.g., `order.completed`) | | `created_at` | string | ISO 8601 timestamp | | `data` | object | Serialized resource (see payloads below) | | `metadata` | object | Additional context including Spree version | Event payloads use the same [Store API V3 serializers](/docs/api-reference/introduction) as the REST API. All `id` fields use [prefixed IDs](/docs/api-reference/introduction) (e.g., `or_m3Rp9wXz`, `prod_86Rf07xd4z`). All monetary values are strings. All timestamps are ISO 8601. For details on creating webhook endpoints and verifying signatures, see [Webhooks](/docs/developer/core-concepts/webhooks). For the event system internals and subscriber pattern, see [Events](/docs/developer/core-concepts/events). Event payloads include the same top-level attributes and unconditional associations as API responses. Conditional associations (like product variants, media, or custom fields) are not included in event payloads. *** ## Order Events Events: `order.created`, `order.updated`, `order.completed`, `order.canceled`, `order.resumed`, `order.paid`, `order.shipped` Order payloads include nested `items`, `fulfillments` (the Store API name for shipments), `payments`, `billing_address`, `shipping_address`, `payment_methods`, and `discounts`. ```json theme={"theme":"night-owl"} { "id": "or_m3Rp9wXz", "number": "R123456789", "state": "complete", "token": "abc123def456", "email": "customer@example.com", "special_instructions": null, "currency": "USD", "item_count": 3, "fulfillment_status": "shipped", "payment_state": "paid", "item_total": "89.99", "display_item_total": "$89.99", "delivery_total": "10.00", "display_delivery_total": "$10.00", "adjustment_total": "0.00", "display_adjustment_total": "$0.00", "promo_total": "0.00", "display_promo_total": "$0.00", "tax_total": "0.00", "display_tax_total": "$0.00", "included_tax_total": "0.00", "display_included_tax_total": "$0.00", "additional_tax_total": "0.00", "display_additional_tax_total": "$0.00", "total": "99.99", "display_total": "$99.99", "completed_at": "2025-01-15T10:30:00Z", "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:30:00Z", "promotions": [], "items": [ { "id": "li_7xRt4wPq", "variant_id": "var_k5nR8xLq", "quantity": 2, "currency": "USD", "name": "Spree Tote Bag", "slug": "spree-tote-bag", "options_text": "Size: M, Color: Black", "price": "29.99", "display_price": "$29.99", "total": "59.98", "display_total": "$59.98", "thumbnail_url": "https://cdn.example.com/images/tote-bag.jpg", "option_values": [], "digital_links": [], "..." } ], "fulfillments": [ { "id": "ful_9xPq4wMn", "number": "H123456789", "status": "shipped", "fulfillment_type": "shipping", "tracking": "1Z999AA10123456784", "tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=1Z999AA10123456784", "cost": "10.00", "display_cost": "$10.00", "fulfilled_at": "2025-01-16T14:00:00Z", "delivery_method": { "id": "dm_2wMn7xRt", "..." }, "stock_location": { "id": "sl_2wMn7xRt", "..." }, "delivery_rates": [], "..." } ], "payments": [ { "id": "pay_3wXz7mRp", "state": "completed", "number": "P123456", "amount": "99.99", "display_amount": "$99.99", "response_code": "ch_abc123", "payment_method_id": "pm_4xLq8nRt", "source_type": "credit_card", "source_id": "cc_5wPq9mXz", "source": { "..." }, "payment_method": { "id": "pm_4xLq8nRt", "..." }, "..." } ], "bill_address": { "id": "addr_1xPq2wMn", "..." }, "ship_address": { "id": "addr_2wMn3xPq", "..." }, "payment_methods": [ { "id": "pm_4xLq8nRt", "..." } ] } ``` ## Line Item Events Events: `line_item.created`, `line_item.updated`, `line_item.deleted` Line item payloads include nested `option_values` and `digital_links`. ```json theme={"theme":"night-owl"} { "id": "li_7xRt4wPq", "variant_id": "var_k5nR8xLq", "quantity": 2, "currency": "USD", "name": "Spree Tote Bag", "slug": "spree-tote-bag", "options_text": "Size: M, Color: Black", "price": "29.99", "display_price": "$29.99", "total": "59.98", "display_total": "$59.98", "adjustment_total": "0.00", "display_adjustment_total": "$0.00", "additional_tax_total": "2.40", "display_additional_tax_total": "$2.40", "included_tax_total": "0.00", "display_included_tax_total": "$0.00", "promo_total": "-5.00", "display_promo_total": "-$5.00", "pre_tax_amount": "54.98", "display_pre_tax_amount": "$54.98", "discounted_amount": "54.98", "display_discounted_amount": "$54.98", "compare_at_amount": null, "display_compare_at_amount": null, "thumbnail_url": "https://cdn.example.com/images/tote-bag.jpg", "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:00:00Z", "option_values": [ { "id": "ov_2wMn9xPq", "name": "M", "presentation": "Medium" } ], "digital_links": [] } ``` ## Payment Events Events: `payment.created`, `payment.updated`, `payment.paid` Payment payloads include a nested `payment_method` and polymorphic `source` (credit card, store credit, or payment source). ```json theme={"theme":"night-owl"} { "id": "pay_3wXz7mRp", "state": "completed", "number": "P123456", "amount": "99.99", "display_amount": "$99.99", "response_code": "ch_abc123", "payment_method_id": "pm_4xLq8nRt", "source_type": "credit_card", "source_id": "cc_5wPq9mXz", "source": { "id": "cc_5wPq9mXz", "..." }, "payment_method": { "id": "pm_4xLq8nRt", "..." }, "created_at": "2025-01-15T10:25:00Z", "updated_at": "2025-01-15T10:25:00Z" } ``` The `source_type` field is normalized to one of: `"credit_card"`, `"store_credit"`, `"payment_source"`, or `null`. ## Payment Session Events Events: `payment_session.created`, `payment_session.updated`, `payment_session.deleted` Payment session payloads include a nested `payment_method` and optionally a nested `payment`. ```json theme={"theme":"night-owl"} { "id": "ps_6nRt2xLq", "status": "pending", "amount": "99.99", "currency": "USD", "external_id": "pi_3abc123", "external_data": {}, "customer_external_id": null, "order_id": "or_m3Rp9wXz", "payment_method_id": "pm_4xLq8nRt", "payment_method": { "id": "pm_4xLq8nRt", "..." }, "expires_at": "2025-01-15T11:00:00Z", "created_at": "2025-01-15T10:25:00Z", "updated_at": "2025-01-15T10:25:00Z" } ``` ## Payment Setup Session Events Events: `payment_setup_session.created`, `payment_setup_session.updated`, `payment_setup_session.deleted` Payment setup session payloads include a nested `payment_method`. ```json theme={"theme":"night-owl"} { "id": "pss_8mXz3wPq", "status": "pending", "external_id": "seti_abc123", "external_client_secret": "seti_abc123_secret_xyz", "external_data": {}, "payment_method_id": "pm_4xLq8nRt", "payment_source_id": "cc_5wPq9mXz", "payment_source_type": "Spree::CreditCard", "customer_id": "usr_k5nR8xLq", "payment_method": { "id": "pm_4xLq8nRt", "..." }, "created_at": "2025-01-15T10:25:00Z", "updated_at": "2025-01-15T10:25:00Z" } ``` ## Shipment Events Events: `shipment.created`, `shipment.updated`, `shipment.shipped`, `shipment.canceled`, `shipment.resumed` The Store API/SDK exposes shipments as `fulfillments`, and webhook payloads use the same V3 serializer. Payloads include nested `delivery_method`, `stock_location`, and `delivery_rates`. ```json theme={"theme":"night-owl"} { "id": "ful_9xPq4wMn", "number": "H123456789", "status": "shipped", "fulfillment_type": "shipping", "tracking": "1Z999AA10123456784", "tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=1Z999AA10123456784", "cost": "10.00", "display_cost": "$10.00", "fulfilled_at": "2025-01-16T14:00:00Z", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-16T14:00:00Z", "delivery_method": { "id": "dm_2wMn7xRt", "..." }, "stock_location": { "id": "sl_2wMn7xRt", "..." }, "delivery_rates": [] } ``` ## Product Events Events: `product.created`, `product.updated`, `product.deleted`, `product.activate`, `product.archive`, `product.out_of_stock`, `product.back_in_stock` Product event payloads include pricing, stock status, and availability flags. Conditional associations (variants, media, option types, categories, custom fields) are not included in event payloads. ```json theme={"theme":"night-owl"} { "id": "prod_86Rf07xd4z", "name": "Spree Tote Bag", "slug": "spree-tote-bag", "description": "A beautiful tote bag", "meta_description": null, "meta_keywords": null, "variant_count": 3, "default_variant_id": "var_k5nR8xLq", "thumbnail_url": "https://cdn.example.com/images/tote-bag.jpg", "purchasable": true, "in_stock": true, "backorderable": false, "available": true, "tags": ["summer", "accessories"], "price": { "id": "pri_4wPq9mXz", "amount": "29.99", "amount_in_cents": 2999, "display_amount": "$29.99", "compare_at_amount": null, "compare_at_amount_in_cents": null, "display_compare_at_amount": null, "currency": "USD", "price_list_id": null }, "original_price": null, "available_on": "2025-01-01T00:00:00Z", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-15T10:00:00Z" } ``` ## Variant Events Events: `variant.created`, `variant.updated`, `variant.deleted` Variant event payloads include pricing, stock status, and always-included `option_values`. Conditional associations (media, custom fields) are not included in event payloads. ```json theme={"theme":"night-owl"} { "id": "var_k5nR8xLq", "product_id": "prod_86Rf07xd4z", "sku": "SPR-TOTE-BLK", "is_master": false, "options_text": "Size: M, Color: Black", "track_inventory": true, "media_count": 2, "thumbnail": "https://cdn.example.com/images/tote-bag-black.jpg", "purchasable": true, "in_stock": true, "backorderable": false, "weight": 0.5, "height": 40.0, "width": 35.0, "depth": 10.0, "price": { "id": "pri_4wPq9mXz", "amount": "29.99", "amount_in_cents": 2999, "display_amount": "$29.99", "compare_at_amount": null, "compare_at_amount_in_cents": null, "display_compare_at_amount": null, "currency": "USD", "price_list_id": null }, "original_price": null, "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-15T10:00:00Z", "option_values": [ { "id": "ov_2wMn9xPq", "name": "M", "presentation": "Medium" }, { "id": "ov_3xLq8nRt", "name": "Black", "presentation": "Black" } ] } ``` ## Price Events Events: `price.created`, `price.updated`, `price.deleted` ```json theme={"theme":"night-owl"} { "id": "pri_4wPq9mXz", "amount": "29.99", "amount_in_cents": 2999, "display_amount": "$29.99", "compare_at_amount": "39.99", "compare_at_amount_in_cents": 3999, "display_compare_at_amount": "$39.99", "currency": "USD", "price_list_id": null } ``` ## Image Events Events: `image.created`, `image.updated`, `image.deleted` Image payloads include URLs for all configured image variants (mini, small, medium, large, xlarge). ```json theme={"theme":"night-owl"} { "id": "img_5mXz3wPq", "type": "Spree::Image", "viewable_type": "Spree::Variant", "viewable_id": "var_k5nR8xLq", "position": 1, "alt": "Black tote bag front view", "original_url": "https://cdn.example.com/images/original.jpg", "mini_url": "https://cdn.example.com/images/mini.jpg", "small_url": "https://cdn.example.com/images/small.jpg", "medium_url": "https://cdn.example.com/images/medium.jpg", "large_url": "https://cdn.example.com/images/large.jpg", "xlarge_url": "https://cdn.example.com/images/xlarge.jpg", "og_image_url": "https://cdn.example.com/images/og.jpg", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z" } ``` ## Stock Item Events Events: `stock_item.created`, `stock_item.updated`, `stock_item.deleted` ```json theme={"theme":"night-owl"} { "id": "si_6nRt2xLq", "count_on_hand": 25, "backorderable": false, "stock_location_id": "sl_2wMn7xRt", "variant_id": "var_k5nR8xLq", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-15T10:00:00Z" } ``` ## Stock Movement Events Events: `stock_movement.created`, `stock_movement.updated`, `stock_movement.deleted` ```json theme={"theme":"night-owl"} { "id": "sm_7xRt4wPq", "quantity": -1, "action": "sold", "originator_type": "Spree::Shipment", "originator_id": "ful_9xPq4wMn", "stock_item_id": "si_6nRt2xLq", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:00Z" } ``` ## Stock Transfer Events Events: `stock_transfer.created`, `stock_transfer.updated`, `stock_transfer.deleted` ```json theme={"theme":"night-owl"} { "id": "st_8mXz3wPq", "number": "T123456789", "type": "Spree::StockTransfer", "reference": "Warehouse rebalance", "source_location_id": "sl_2wMn7xRt", "destination_location_id": "sl_9xPq4wMn", "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:00:00Z" } ``` ## Customer Events Events: `customer.created`, `customer.updated`, `customer.deleted` Customer payloads include nested `addresses`, `default_billing_address`, and `default_shipping_address`. ```json theme={"theme":"night-owl"} { "id": "usr_k5nR8xLq", "email": "customer@example.com", "first_name": "John", "last_name": "Doe", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-15T10:00:00Z", "addresses": [], "default_billing_address": null, "default_shipping_address": null } ``` ## Promotion Events Events: `promotion.created`, `promotion.updated`, `promotion.deleted` ```json theme={"theme":"night-owl"} { "id": "promo_2wMn9xPq", "name": "Summer Sale 20% Off", "description": "20% off all summer items", "code": "SUMMER20", "type": "Spree::Promotion", "kind": "coupon", "path": null, "match_policy": "all", "usage_limit": 1000, "advertise": true, "multi_codes": false, "code_prefix": null, "number_of_codes": null, "starts_at": "2025-06-01T00:00:00Z", "expires_at": "2025-08-31T23:59:59Z", "promotion_category_id": "pcat_3xLq8nRt", "created_at": "2025-05-15T10:00:00Z", "updated_at": "2025-05-15T10:00:00Z" } ``` ## Gift Card Events Events: `gift_card.created`, `gift_card.updated`, `gift_card.deleted` ```json theme={"theme":"night-owl"} { "id": "gc_4xLq8nRt", "code": "****-1234", "state": "active", "amount": 50.0, "amount_used": 15.0, "amount_authorized": 0.0, "amount_remaining": 35.0, "display_amount": "$50.00", "display_amount_used": "$15.00", "display_amount_remaining": "$35.00", "currency": "USD", "expired": false, "active": true, "expires_at": "2026-01-01T00:00:00Z", "redeemed_at": "2025-02-01T10:00:00Z", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-02-01T10:00:00Z" } ``` The gift card `code` is displayed as a masked value (e.g., `****-1234`) for security. ## Gift Card Batch Events Events: `gift_card_batch.created`, `gift_card_batch.updated`, `gift_card_batch.deleted` ```json theme={"theme":"night-owl"} { "id": "gcb_5wPq9mXz", "codes_count": 100, "amount": "25.00", "currency": "USD", "prefix": "HOLIDAY", "expires_at": "2026-12-31T00:00:00Z", "created_by_id": "adm_8mXz3wPq", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z" } ``` ## Store Credit Events Events: `store_credit.created`, `store_credit.updated`, `store_credit.deleted` ```json theme={"theme":"night-owl"} { "id": "sc_6nRt2xLq", "amount": "100.00", "amount_used": "25.00", "amount_remaining": "75.00", "display_amount": "$100.00", "display_amount_used": "$25.00", "display_amount_remaining": "$75.00", "currency": "USD" } ``` ## Refund Events Events: `refund.created`, `refund.updated`, `refund.deleted` ```json theme={"theme":"night-owl"} { "id": "ref_7xRt4wPq", "amount": "29.99", "transaction_id": "txn_abc123", "payment_id": "pay_3wXz7mRp", "refund_reason_id": "rr_2wMn9xPq", "reimbursement_id": "rei_3xLq8nRt", "created_at": "2025-01-20T10:00:00Z", "updated_at": "2025-01-20T10:00:00Z" } ``` ## Reimbursement Events Events: `reimbursement.created`, `reimbursement.updated`, `reimbursement.deleted` ```json theme={"theme":"night-owl"} { "id": "rei_3xLq8nRt", "number": "RI123456789", "reimbursement_status": "reimbursed", "total": "29.99", "order_id": "or_m3Rp9wXz", "customer_return_id": "cr_4xLq8nRt", "created_at": "2025-01-20T10:00:00Z", "updated_at": "2025-01-20T10:00:00Z" } ``` ## Return Authorization Events Events: `return_authorization.created`, `return_authorization.updated`, `return_authorization.deleted` ```json theme={"theme":"night-owl"} { "id": "ra_8mXz3wPq", "number": "RA123456789", "state": "authorized", "order_id": "or_m3Rp9wXz", "stock_location_id": "sl_2wMn7xRt", "return_authorization_reason_id": "rar_9xPq4wMn", "created_at": "2025-01-18T10:00:00Z", "updated_at": "2025-01-18T10:00:00Z" } ``` ## Return Item Events Events: `return_item.created`, `return_item.updated`, `return_item.deleted` ```json theme={"theme":"night-owl"} { "id": "ri_9xPq4wMn", "reception_status": "received", "acceptance_status": "accepted", "pre_tax_amount": "29.99", "included_tax_total": "0.00", "additional_tax_total": "2.40", "inventory_unit_id": "iu_2wMn7xRt", "return_authorization_id": "ra_8mXz3wPq", "customer_return_id": "cr_4xLq8nRt", "reimbursement_id": "rei_3xLq8nRt", "exchange_variant_id": null, "created_at": "2025-01-19T10:00:00Z", "updated_at": "2025-01-19T10:00:00Z" } ``` ## Customer Return Events Events: `customer_return.created`, `customer_return.updated`, `customer_return.deleted` ```json theme={"theme":"night-owl"} { "id": "cr_4xLq8nRt", "number": "CR123456789", "stock_location_id": "sl_2wMn7xRt", "created_at": "2025-01-19T10:00:00Z", "updated_at": "2025-01-19T10:00:00Z" } ``` ## Wishlist Events Events: `wishlist.created`, `wishlist.updated`, `wishlist.deleted` Wishlist items are a conditional association and are not included in event payloads. ```json theme={"theme":"night-owl"} { "id": "wl_5wPq9mXz", "name": "My Wishlist", "token": "abc123def456", "is_default": true, "is_private": true, "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-15T10:00:00Z" } ``` ## Wished Item Events Events: `wished_item.created`, `wished_item.updated`, `wished_item.deleted` Wished item payloads include a nested `variant`. ```json theme={"theme":"night-owl"} { "id": "wi_6nRt2xLq", "variant_id": "var_k5nR8xLq", "wishlist_id": "wl_5wPq9mXz", "quantity": 1, "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:00:00Z", "variant": { "id": "var_k5nR8xLq", "..." } } ``` ## Post Events Events: `post.created`, `post.updated`, `post.deleted` ```json theme={"theme":"night-owl"} { "id": "post_7xRt4wPq", "title": "Summer Collection 2025", "slug": "summer-collection-2025", "meta_title": "Summer Collection | My Store", "meta_description": "Discover our new summer collection", "published_at": "2025-06-01T00:00:00Z", "author_id": "adm_8mXz3wPq", "post_category_id": "pcat_9xPq4wMn", "created_at": "2025-05-15T10:00:00Z", "updated_at": "2025-05-15T10:00:00Z" } ``` ## Post Category Events Events: `post_category.created`, `post_category.updated`, `post_category.deleted` ```json theme={"theme":"night-owl"} { "id": "pcat_9xPq4wMn", "title": "News", "slug": "news", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z" } ``` ## Newsletter Subscriber Events Events: `newsletter_subscriber.created`, `newsletter_subscriber.updated`, `newsletter_subscriber.deleted` ```json theme={"theme":"night-owl"} { "id": "ns_2wMn9xPq", "email": "subscriber@example.com", "verified": true, "verified_at": "2025-01-02T10:00:00Z", "user_id": "usr_k5nR8xLq", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-02T10:00:00Z" } ``` ## Digital Product Events Events: `digital.created`, `digital.updated`, `digital.deleted` ```json theme={"theme":"night-owl"} { "id": "dig_3xLq8nRt", "variant_id": "var_k5nR8xLq", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z" } ``` ## Digital Link Events Events: `digital_link.created`, `digital_link.updated`, `digital_link.deleted` ```json theme={"theme":"night-owl"} { "id": "dl_4xLq8nRt", "access_counter": 3, "filename": "ebook.pdf", "content_type": "application/pdf", "download_url": "/api/v3/store/digital_downloads/abc123", "authorizable": true, "expired": false, "access_limit_exceeded": false, "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T12:00:00Z" } ``` ## Import Events Events: `import.created`, `import.updated`, `import.deleted` ```json theme={"theme":"night-owl"} { "id": "imp_5wPq9mXz", "number": "I123456789", "type": "Spree::Imports::Products", "status": "completed", "owner_type": "Spree::Store", "owner_id": "str_9xPq2wMn", "user_id": "adm_8mXz3wPq", "rows_count": 150, "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:05:00Z" } ``` ## Import Row Events Events: `import_row.created`, `import_row.updated`, `import_row.deleted` ```json theme={"theme":"night-owl"} { "id": "ir_6nRt2xLq", "import_id": "imp_5wPq9mXz", "row_number": 42, "status": "success", "validation_errors": [], "item_type": "Spree::Product", "item_id": "prod_86Rf07xd4z", "created_at": "2025-01-15T10:01:00Z", "updated_at": "2025-01-15T10:01:00Z" } ``` ## Export Events Events: `export.created`, `export.updated`, `export.deleted` ```json theme={"theme":"night-owl"} { "id": "exp_7xRt4wPq", "number": "E123456789", "type": "Spree::Exports::Products", "format": "csv", "user_id": "adm_8mXz3wPq", "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:02:00Z" } ``` ## Report Events Events: `report.created`, `report.updated`, `report.deleted` ```json theme={"theme":"night-owl"} { "id": "rep_8mXz3wPq", "type": "Spree::Reports::SalesByProduct", "user_id": "adm_8mXz3wPq", "currency": "USD", "date_from": "2025-01-01T00:00:00Z", "date_to": "2025-01-31T23:59:59Z", "created_at": "2025-02-01T10:00:00Z", "updated_at": "2025-02-01T10:00:00Z" } ``` ## Invitation Events Events: `invitation.created`, `invitation.updated`, `invitation.deleted` ```json theme={"theme":"night-owl"} { "id": "inv_9xPq4wMn", "email": "newadmin@example.com", "status": "pending", "resource_type": "Spree::Store", "resource_id": "str_9xPq2wMn", "inviter_type": "Spree::AdminUser", "inviter_id": "adm_8mXz3wPq", "invitee_type": null, "invitee_id": null, "role_id": "role_2wMn9xPq", "expires_at": "2025-02-15T10:00:00Z", "accepted_at": null, "created_at": "2025-01-15T10:00:00Z", "updated_at": "2025-01-15T10:00:00Z" } ``` # Admin Dashboard Source: https://spreecommerce.org/docs/developer/admin/admin Customize and extend the Spree Admin Dashboard to manage products, orders, customers, promotions, and store settings for your ecommerce platform. The Spree Admin Dashboard is a full-featured administration interface for managing your e-commerce store. Spree Admin Dashboard ## What You Can Do The Admin Dashboard allows store administrators to manage: * **Products** - Create and manage products, variants, images, and inventory * **Orders** - Process orders, refunds, and shipments * **Customers** - View customer accounts and order history * **Promotions** - Create discounts, coupon codes, and special offers * **Settings** - Configure store settings, shipping, taxes, and payments ## Customization Options As a developer, you can fully customize the Admin Dashboard: Create entirely new admin pages for custom features like Brands, Vendors, or any custom model Inject custom fields, buttons, and sections into existing admin pages Add menu items to the sidebar or create custom navigation structures Add your own CSS to match your brand or modify the look and feel ## Architecture Overview Spree Admin follows standard Rails conventions with some Spree-specific patterns: ``` app/ ├── controllers/ │ └── spree/ │ └── admin/ │ └── products_controller.rb # Your custom controllers ├── views/ │ └── spree/ │ └── admin/ │ └── products/ │ ├── index.html.erb # List view │ ├── _form.html.erb # Shared form partial │ └── _table_row.html.erb # Table row partial ├── helpers/ │ └── spree/ │ └── admin/ │ └── products_helper.rb # View helpers └── assets/ └── tailwind/ └── spree_admin.css # Custom Tailwind styles ``` ## Key Concepts ### Controllers Admin controllers inherit from `Spree::Admin::ResourceController` which provides: * Full CRUD operations (index, new, create, edit, update, destroy) * Automatic authorization checks * Flash messages and redirects * Search and filtering with Ransack ```ruby theme={"theme":"night-owl"} # app/controllers/spree/admin/brands_controller.rb module Spree module Admin class BrandsController < ResourceController # That's it! CRUD is handled automatically. # Override methods only when needed. end end end ``` Learn more about creating admin sections in the [Admin Dashboard Tutorial](/docs/developer/tutorial/admin). ### Views & Templates Views use standard Rails ERB templates with Spree's [Form Builder](/docs/developer/admin/form-builder) and [Components](/docs/developer/admin/components): ```erb theme={"theme":"night-owl"} <%# app/views/spree/admin/brands/_form.html.erb %>
<%= Spree.t(:general_settings) %>
<%= f.spree_text_field :name, required: true %> <%= f.spree_text_area :description %> <%= f.spree_file_field :logo, width: 300, height: 300 %> <%= f.spree_check_box :active %>
``` ### JavaScript & Interactivity Spree Admin uses [Hotwire](https://hotwire.dev/) (Turbo + Stimulus) for interactivity: * **Turbo Drive** - Fast page navigation without full reloads * **Turbo Frames** - Update parts of the page independently * **Turbo Streams** - Real-time updates over WebSocket * **Stimulus** - Lightweight JavaScript controllers ```erb theme={"theme":"night-owl"} <%# Using Stimulus controllers %>
...
``` Add custom JavaScript using [Stimulus controllers](/docs/developer/admin/custom-javascript). ### Styling The Admin Dashboard uses [Tailwind CSS v4](https://tailwindcss.com/) for styling. You can: * Use Tailwind utility classes directly in your views * Override theme variables (colors, spacing, typography) * Add custom components using `@layer components` ```erb theme={"theme":"night-owl"} <%# Using Tailwind utility classes in views %>

My Custom Card

Card content here

``` Learn how to customize styles in [Custom CSS](/docs/developer/admin/custom-css). ## Quick Reference ### Available Tools | Tool | Purpose | Documentation | | -------------- | --------------------------------------- | ------------------------------------------------- | | Form Builder | Create consistent forms with validation | [Form Builder](/docs/developer/admin/form-builder) | | Components | Dropdowns, dialogs, icons, badges, etc. | [Components](/docs/developer/admin/components) | | Helper Methods | Navigation, links, utilities | [Helper Methods](/docs/developer/admin/helper-methods) | | UI Extensions | Inject content into existing pages | [Extending UI](/docs/developer/admin/extending-ui) | ### Common Tasks | Task | How To | | ---------------------- | --------------------------------------------------------------------------- | | Add a new admin page | Use the scaffold generator: `bin/rails g spree:admin:scaffold Spree::Brand` | | Add sidebar navigation | Use `Spree.admin.navigation.sidebar.add` in an initializer | | Add form fields | Use `f.spree_text_field`, `f.spree_select`, etc. | | Show flash messages | They're automatic with `ResourceController` | | Check permissions | Use `can?(:update, @product)` in views | ### Generator Commands ```bash Spree CLI (Docker) theme={"theme":"night-owl"} # Generate a complete admin section (controller, views, routes) spree generate spree:admin:scaffold Spree::Brand # Generate just a controller spree generate controller Spree::Admin::Brands --skip-routes ``` ```bash Without Spree CLI theme={"theme":"night-owl"} # Generate a complete admin section (controller, views, routes) bin/rails g spree:admin:scaffold Spree::Brand # Generate just a controller bin/rails g controller Spree::Admin::Brands --skip-routes ``` ## Authentication & Authorization ### Authentication Admin users must be authenticated to access the dashboard. Spree supports: * Built-in Devise authentication * Custom authentication adapters * SSO integration See [Authentication](/docs/developer/admin/authentication) for setup details. ### Authorization Spree uses [CanCanCan](https://github.com/CanCanCommunity/cancancan) for authorization: ```ruby theme={"theme":"night-owl"} # Check permissions in controllers authorize! :update, @product # Check permissions in views <% if can?(:destroy, @product) %> <%= link_to_delete(@product) %> <% end %> ``` See [Permissions](/docs/developer/customization/permissions) for defining custom abilities. ## Next Steps Step-by-step guide to creating a complete admin section Learn all available form field helpers Explore UI components like dropdowns, dialogs, and icons Add custom content to existing admin pages # Admin Panel Authentication Source: https://spreecommerce.org/docs/developer/admin/authentication How to customize the Spree admin panel authentication Spree allows you to use a different model for the admin panel than the storefront (that's the default since Spree 5.2). Here you can find how to customize the admin panel authentication to use a different model. Let's assume you have an existing `AdminUser` model in your application and you're using Devise for authentication. In `config/initializers/spree.rb` file, add the following line: ```ruby theme={"theme":"night-owl"} Spree.admin_user_class = 'AdminUser' ``` This will tell Spree to use your `AdminUser` model for the admin panel. You will also need to add the following line in that model file: ```ruby theme={"theme":"night-owl"} include Spree::UserMethods ``` In your `config/initializers/routes.rb` file, you will need to define devise routes for the 2nd model: ```ruby theme={"theme":"night-owl"} Spree::Core::Engine.routes.prepend_routes do # Admin authentication devise_for( Spree.admin_user_class.model_name.singular_route_key, class_name: Spree.admin_user_class.to_s, controllers: { sessions: 'spree/admin/user_sessions', passwords: 'spree/admin/user_passwords' }, skip: :registrations, path: :admin_user, router_name: :spree ) end ``` And now in your `lib/spree/authentication_helpers.rb` file, please replace the following lines: ```diff theme={"theme":"night-owl"} def spree_admin_login_path(opts = {}) - spree_login_path(opts) + new_admin_user_session_path(opts) end def spree_admin_logout_path(opts = {}) - spree_logout_path(opts) + destroy_admin_user_session_path(opts) end ``` Now when attempting to access the admin panel, you will be redirected to the dedicated admin panel login page. Admin panel login page # Admin Components Source: https://spreecommerce.org/docs/developer/admin/components Spree Admin provides a set of reusable UI components that you can use in your custom admin views. These components are implemented as view helpers and integrate with Stimulus controllers for interactivity. ## Dropdown The dropdown component creates accessible dropdown menus with automatic positioning using Floating UI. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= dropdown do %> <%= dropdown_toggle class: 'btn-light btn-sm' do %> <%= icon('dots-vertical', class: 'mr-0') %> <% end %> <%= dropdown_menu do %> <%= link_to_with_icon 'pencil', Spree.t(:edit), edit_path, class: 'dropdown-item' %> <%= link_to_with_icon 'trash', Spree.t(:delete), delete_path, class: 'dropdown-item text-danger', data: { turbo_method: :delete, turbo_confirm: Spree.t(:are_you_sure) } %> <% end %> <% end %> ``` ### Features * **Automatic positioning** - Uses Floating UI to position the menu optimally * **Auto-flip** - Menu flips to stay within viewport * **Click outside to close** - Menu closes when clicking outside * **Escape to close** - Menu closes when pressing Escape key * **Keyboard navigation** - Full keyboard accessibility ### `dropdown` Creates the dropdown container with Stimulus controller. ```erb theme={"theme":"night-owl"} <%= dropdown do %> <% end %> <%= dropdown placement: 'top-end' do %> <% end %> ``` | Option | Type | Default | Description | | ----------- | ------- | -------------- | --------------------------------------------------------------------------------- | | `class` | String | - | Additional CSS classes | | `placement` | String | `bottom-start` | Menu placement: `bottom-start`, `bottom-end`, `top-start`, `top-end` | | `direction` | String | - | Legacy option: `left` → `bottom-end`, `top` → `top-start`, `top-left` → `top-end` | | `portal` | Boolean | - | Whether to portal the dropdown to body | | `data` | Hash | - | Additional data attributes | ### `dropdown_toggle` Creates the button that toggles the dropdown menu. ```erb theme={"theme":"night-owl"} <%= dropdown_toggle class: 'btn-primary' do %> Actions <%= icon('chevron-down', class: 'ml-2') %> <% end %> ``` | Option | Type | Default | Description | | ------- | ------ | ------- | -------------------------------------------------- | | `class` | String | - | Additional CSS classes (added to `btn` base class) | | `data` | Hash | - | Additional data attributes | ### `dropdown_menu` Creates the menu container for dropdown items. ```erb theme={"theme":"night-owl"} <%= dropdown_menu do %> <%= link_to 'Option 1', '#', class: 'dropdown-item' %> <%= link_to 'Option 2', '#', class: 'dropdown-item text-danger' %> <% end %> ``` | Option | Type | Default | Description | | ------- | ------ | ------- | -------------------------- | | `class` | String | - | Additional CSS classes | | `data` | Hash | - | Additional data attributes | ### Complete Example ```erb theme={"theme":"night-owl"} <%= content_for :page_actions do %> <%= dropdown do %> <%= dropdown_toggle class: 'btn-primary' do %> <%= icon('settings', class: 'mr-2') %> Actions <%= icon('chevron-down', class: 'ml-2') %> <% end %> <%= dropdown_menu do %> <%= link_to_with_icon 'download', 'Export CSV', export_path(format: :csv), class: 'dropdown-item' %> <%= link_to_with_icon 'file-export', 'Export Excel', export_path(format: :xlsx), class: 'dropdown-item' %> <%= link_to_with_icon 'upload', 'Import', import_path, class: 'dropdown-item' %> <%= link_to_with_icon 'trash', 'Delete All', bulk_delete_path, class: 'dropdown-item text-danger', data: { turbo_method: :delete, turbo_confirm: Spree.t(:are_you_sure) } %> <% end %> <% end %> <% end %> ``` ## Dialog Dialogs (modals) are used for focused interactions that require user attention. They overlay the page content and must be dismissed before continuing. ### Basic Usage ```erb theme={"theme":"night-owl"}
<%= dialog_header('Edit Product') %>
``` ### `dialog_header` Creates a dialog header with title and close button. ```erb theme={"theme":"night-owl"} <%= dialog_header('Confirm Action') %> <%= dialog_header('Custom Dialog', 'my-dialog') %> ``` | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | --------------------------------------------- | | `title` | String | required | The dialog title | | `controller_name` | String | `dialog` | Stimulus controller name for the close action | **Renders:** ```html theme={"theme":"night-owl"}
Confirm Action
``` ### `dialog_close_button` Creates a standalone close button for dialogs. ```erb theme={"theme":"night-owl"} <%= dialog_close_button %> <%= dialog_close_button('custom-controller') %> ``` | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | ------------------------ | | `controller_name` | String | `dialog` | Stimulus controller name | ### `dialog_discard_button` Creates a "Discard" button that closes the dialog. ```erb theme={"theme":"night-owl"} <%= dialog_discard_button %> ``` | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | ------------------------ | | `controller_name` | String | `dialog` | Stimulus controller name | **Renders:** ```html theme={"theme":"night-owl"} ``` ### Complete Dialog Example ```erb theme={"theme":"night-owl"} <%= turbo_frame_tag 'main-dialog' do %>
<%= dialog_header('Add New Item') %> <%= form_with model: @item, url: items_path, data: { turbo_frame: '_top' } do |f| %>
<%= f.spree_text_field :name, required: true %> <%= f.spree_text_area :description %>
<% end %>
<% end %> ``` ## Drawer Drawers are slide-out panels typically used for filters, secondary forms, or detailed views without leaving the current page. ### Basic Usage ```erb theme={"theme":"night-owl"}
<%= drawer_header('Filter Options') %>
``` ### `drawer_header` Creates a drawer header with title and close button. ```erb theme={"theme":"night-owl"} <%= drawer_header('Filters') %> <%= drawer_header('Details', 'custom-drawer') %> ``` | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | --------------------------------------------- | | `title` | String | required | The drawer title | | `controller_name` | String | `drawer` | Stimulus controller name for the close action | **Renders:** ```html theme={"theme":"night-owl"}
Filters
``` ### `drawer_close_button` Creates a standalone close button for drawers. ```erb theme={"theme":"night-owl"} <%= drawer_close_button %> <%= drawer_close_button('custom-drawer') %> ``` | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | ------------------------ | | `controller_name` | String | `drawer` | Stimulus controller name | ### `drawer_discard_button` Creates a "Discard" button that closes the drawer. ```erb theme={"theme":"night-owl"} <%= drawer_discard_button %> ``` | Parameter | Type | Default | Description | | ----------------- | ------ | -------- | ------------------------ | | `controller_name` | String | `drawer` | Stimulus controller name | ## Icon Icons are rendered using the [Tabler Icons](https://tabler.io/icons) library. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= icon('package') %> <%= icon('shopping-cart', class: 'text-primary') %> <%= icon('check', class: 'text-success', height: 24) %> ``` ### Options | Option | Type | Default | Description | | -------- | ------- | ------- | ------------------------ | | `class` | String | - | Additional CSS classes | | `height` | Integer | - | Icon size in pixels | | `style` | String | - | Additional inline styles | ### Output ```html theme={"theme":"night-owl"} ``` ### Legacy Icon Names For backwards compatibility, legacy icon names are automatically translated: | Legacy Name | Tabler Icon | | ----------- | --------------- | | `save` | `device-floppy` | | `edit` | `pencil` | | `delete` | `trash` | | `add` | `plus` | | `cancel` | `x` | ### Common Icons | Icon | Name | Usage | | ----- | --------------- | ------------------ | | | `pencil` | Edit actions | | | `trash` | Delete actions | | | `plus` | Add/create actions | | | `eye` | View/preview | | | `download` | Download/export | | | `upload` | Upload/import | | | `check` | Success/confirm | | | `x` | Close/cancel | | | `dots-vertical` | More options | | | `chevron-down` | Expand | | | `chevron-right` | Navigate | ## Image Displays optimized images with automatic WebP conversion and retina support. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= spree_image_tag(product.images.first, width: 100, height: 100) %> <%= spree_image_tag(current_store.logo, width: 200, height: 60) %> <%= spree_image_tag(taxon.image, width: 400, height: 300, class: 'rounded') %> ``` ### Options | Option | Type | Default | Description | | --------- | ---------------- | -------- | ------------------------------------------ | | `image` | Asset/Attachment | required | `Spree::Asset` or ActiveStorage attachment | | `width` | Integer | - | Display width in pixels | | `height` | Integer | - | Display height in pixels | | `class` | String | - | CSS classes | | `alt` | String | - | Alt text for accessibility | | `loading` | Symbol | - | `:lazy` or `:eager` | ### Features * **Retina support** - Automatically scales dimensions by 2x for sharp display * **WebP conversion** - Outputs optimized WebP format * **Smart cropping** - When both dimensions provided, crops to fill * **Aspect ratio preservation** - When one dimension provided, maintains ratio ### Examples ```erb theme={"theme":"night-owl"} <%# Product thumbnail in index table %> <% if product.has_images? %> <%= spree_image_tag product.default_image, width: 48, height: 48, class: 'rounded' %> <% else %>
<%= icon('photo-off', class: 'text-muted') %>
<% end %> <%# Store logo in header %> <%= spree_image_tag current_store.logo, width: 120, height: 40, alt: current_store.name %> <%# Category image %> <%= spree_image_tag taxon.image, width: 300, height: 200, class: 'card-img-top' %> ``` ### With Placeholder ```erb theme={"theme":"night-owl"} <% if @brand.logo.attached? %> <%= spree_image_tag @brand.logo, width: 100, height: 100 %> <% else %>
<%= icon('photo') %>
<% end %> ``` For comprehensive documentation on image handling, storage configuration, and best practices, see the [Media](/docs/developer/core-concepts/media) guide. ## Tooltip Tooltips provide additional context on hover. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= icon('info-circle') %> <%= tooltip('This is helpful information') %> ``` ### `tooltip` Creates a tooltip container. ```erb theme={"theme":"night-owl"} <%= tooltip('Simple text tooltip') %> <%= tooltip do %> Rich content with formatting <% end %> ``` | Parameter | Type | Description | | --------- | ------ | -------------------------------------------- | | `text` | String | Tooltip text (or use block for rich content) | ### `help_bubble` Creates an info icon with a tooltip - commonly used for form field hints. ```erb theme={"theme":"night-owl"} <%= help_bubble('This field is used for SEO optimization') %> <%= help_bubble('Shown below the field', 'bottom') %> <%= help_bubble('Custom styled', 'top', css: 'text-primary') %> ``` | Parameter | Type | Default | Description | | ----------- | ------ | ----------------------- | -------------------------------------------------- | | `text` | String | required | Tooltip text | | `placement` | String | `top` | Tooltip position: `top`, `bottom`, `left`, `right` | | `css` | String | `text-xs text-muted...` | CSS classes for the icon | **Output:** ```html theme={"theme":"night-owl"} This field is used for SEO optimization ``` ## Active Badge Displays a status badge indicating active/inactive state. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= active_badge(product.active?) %> <%= active_badge(user.confirmed?) %> <%= active_badge(order.paid?, label: 'Paid') %> ``` ### Options | Option | Type | Default | Description | | ----------- | ------- | -------- | ------------------------- | | `condition` | Boolean | required | The condition to evaluate | | `label` | String | Yes/No | Custom label text | ### Output When condition is `true`: ```html theme={"theme":"night-owl"} Yes ``` When condition is `false`: ```html theme={"theme":"night-owl"} No ``` ### Custom Labels ```erb theme={"theme":"night-owl"} <%= active_badge(subscription.active?, label: subscription.active? ? 'Active' : 'Expired') %> <%= active_badge(feature.enabled?, label: feature.enabled? ? 'Enabled' : 'Disabled') %> ``` ## Avatar Renders a user avatar with automatic fallback to initials. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= render_avatar(current_user) %> <%= render_avatar(user, width: 48, height: 48) %> <%= render_avatar(admin, class: 'avatar-lg') %> ``` ### Options | Option | Type | Default | Description | | -------- | ------- | -------- | ------------------------------------------------- | | `user` | Object | required | User object (must respond to `avatar` and `name`) | | `width` | Integer | 128 | Avatar width in pixels | | `height` | Integer | 128 | Avatar height in pixels | | `class` | String | `avatar` | CSS classes | ### Behavior 1. If user has an attached avatar image → displays the image 2. Otherwise → displays user's initials on a colored background ```erb theme={"theme":"night-owl"}
JD
``` ## Clipboard Copy-to-clipboard functionality with visual feedback. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= clipboard_component(product.sku) %> <%= clipboard_component(api_key) %> ``` ### `clipboard_component` Creates a complete clipboard component with hidden input and copy button. ```erb theme={"theme":"night-owl"} <%= clipboard_component('ABC-123-XYZ') %> ``` | Parameter | Type | Description | | --------- | ------ | ---------------- | | `text` | String | The text to copy | **Output:** ```html theme={"theme":"night-owl"} ``` ### `clipboard_button` Creates just the copy button (for custom layouts). ```erb theme={"theme":"night-owl"}
<%= clipboard_button %>
``` ### Inline with Text ```erb theme={"theme":"night-owl"}
<%= product.sku %> <%= clipboard_component(product.sku) %>
``` ## Progress Bar Displays a progress bar with customizable range. ### Basic Usage ```erb theme={"theme":"night-owl"} <%= progress_bar_component(75) %> <%= progress_bar_component(150, max: 200) %> <%= progress_bar_component(50, min: 0, max: 100) %> ``` ### Options | Option | Type | Default | Description | | ------- | ------- | -------- | ---------------------- | | `value` | Integer | required | Current progress value | | `min` | Integer | 0 | Minimum value | | `max` | Integer | 100 | Maximum value | ### Output ```html theme={"theme":"night-owl"}
``` ### Examples ```erb theme={"theme":"night-owl"} <%= progress_bar_component(stock_item.count_on_hand, max: stock_item.backorderable_threshold || 100) %> <%= progress_bar_component(order.shipments.shipped.count, max: order.shipments.count) %> <%= progress_bar_component(uploaded_count, max: total_count) %> ``` ## Date & Time Helpers for displaying dates and times in the user's local timezone. ### `spree_date` Renders a date in the user's local format. ```erb theme={"theme":"night-owl"} <%= spree_date(order.created_at) %> <%= spree_date(product.available_on) %> ``` ### `spree_time` Renders a date and time in the user's local format. ```erb theme={"theme":"night-owl"} <%= spree_time(order.completed_at) %> <%= spree_time(shipment.shipped_at) %> ``` ### `spree_time_ago` Renders a relative time (e.g., "2 hours ago") with a tooltip showing the full timestamp. ```erb theme={"theme":"night-owl"} <%= spree_time_ago(order.completed_at) %> <%= spree_time_ago(comment.created_at) %> ``` **Output:** ```html theme={"theme":"night-owl"} January 15, 2024 10:30 AM ``` ### `local_time` The underlying helper from [local\_time gem](https://github.com/basecamp/local_time) - displays time in user's browser timezone. ```erb theme={"theme":"night-owl"} <%= local_time(order.completed_at) %> <%= local_time(event.starts_at, format: '%B %e, %Y at %l:%M %p') %> ``` ### Comparison | Helper | Output | Use Case | | ---------------- | --------------------------- | -------------------------- | | `spree_date` | "Jan 15, 2024" | Date-only display | | `spree_time` | "Jan 15, 2024 10:30 AM" | Full timestamp | | `spree_time_ago` | "2 hours ago" | Relative time with tooltip | | `local_time` | "January 15, 2024 10:30 AM" | Customizable format | ## Best Practices **Use semantic components** - Choose the right component for the interaction (Dialog for focused tasks, Drawer for contextual panels) **Provide feedback** - Use tooltips and badges to give users context about their actions **Keep dropdowns focused** - Limit dropdown menus to related actions, use dividers to group items **Use appropriate icons** - Choose icons that clearly represent the action **Handle loading states** - Use `turbo_save_button_tag` for forms to show loading feedback **Consider accessibility** - Components include ARIA attributes and keyboard navigation # Admin Dashboard Custom CSS Source: https://spreecommerce.org/docs/developer/admin/custom-css Customize the Spree Admin Dashboard with your own CSS by overriding Tailwind variables, adding custom styles, and extending admin views and helpers. Spree Admin Dashboard uses [Tailwind CSS v4](https://tailwindcss.com/) for styling. The stylesheet system is designed to be easily customizable while maintaining consistency with the Spree design system. ## How It Works When you install Spree Admin, the installer creates a file at `app/assets/tailwind/spree_admin.css` in your application. This file: 1. Imports the base Spree Admin styles from the gem 2. Scans your custom admin views, helpers, and JavaScript for Tailwind classes 3. Allows you to add custom styles and override theme variables ## Using Tailwind Utility Classes You can use any Tailwind CSS utility class in your custom admin views, helpers, or JavaScript files. The build process automatically scans these locations: * `app/views/spree/admin/**/*.erb` * `app/helpers/spree/admin/**/*.rb` * `app/javascript/spree/admin/**/*.js` Example usage in a view: ```erb theme={"theme":"night-owl"}

Custom Section

Your content here

``` ## Customizing the Theme ### Overriding Theme Colors To override Tailwind theme values, add a `@theme` block in your `app/assets/tailwind/spree_admin.css` file after the import: ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} /* Import Spree Admin base styles from the gem */ @import "$SPREE_ADMIN_PATH/app/assets/tailwind/spree/admin/index.css"; /* Override theme colors */ @theme { --color-primary: #4F46E5; /* Change primary color to indigo */ --color-success: #059669; /* Custom success color */ --color-danger: #DC2626; /* Custom danger color */ } ``` ### Available Theme Variables The Spree Admin theme defines these customizable variables: | Variable | Default | Description | | ----------------- | ------------------------- | ------------------------ | | `--color-primary` | `var(--color-zinc-950)` | Primary brand color | | `--color-success` | `var(--color-green-900)` | Success state color | | `--color-danger` | `var(--color-red-600)` | Error/danger state color | | `--color-warning` | `var(--color-yellow-900)` | Warning state color | | `--color-info` | `var(--color-blue-900)` | Info state color | ### Overriding Layout Variables You can also customize layout-related CSS variables: ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} :root { --spacing-sidebar-width: 280px; /* Default: 220px */ --spacing-sidebar-collapsed: 70px; /* Default: 59px */ --spacing-header-height: 64px; /* Default: 58px */ } ``` ## Adding Custom Components Use Tailwind's `@layer` directive to add custom component styles that work seamlessly with the existing design: ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} @layer components { /* Custom card style */ .my-custom-card { @apply bg-white rounded-lg shadow-sm border border-zinc-200 p-4; } /* Custom button variant */ .btn-custom { @apply inline-flex items-center px-4 py-2 rounded-md; @apply bg-indigo-600 text-white font-medium; @apply hover:bg-indigo-700 transition-colors; } /* Custom status badge */ .status-badge { @apply inline-flex items-center px-2 py-1 rounded-full text-xs font-medium; } .status-badge-active { @apply status-badge bg-green-100 text-green-800; } .status-badge-inactive { @apply status-badge bg-zinc-100 text-zinc-600; } } ``` ## Adding Custom Utilities For reusable utility classes, use the utilities layer: ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} @layer utilities { .text-gradient { @apply bg-clip-text text-transparent bg-gradient-to-r from-indigo-500 to-purple-500; } .scrollbar-hidden { -ms-overflow-style: none; scrollbar-width: none; } .scrollbar-hidden::-webkit-scrollbar { display: none; } } ``` ## Scanning Additional Paths If you have admin-related code in non-standard locations, add additional `@source` directives: ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} /* Scan host app's custom admin code for Tailwind classes */ @source "../../views/spree/admin/**/*.erb"; @source "../../helpers/spree/admin/**/*.rb"; @source "../../javascript/spree/admin/**/*.js"; /* Add your custom paths */ @source "../../components/admin/**/*.erb"; @source "../../views/admin/**/*.erb"; ``` ## Development Workflow ### Starting the CSS Watcher During development, run the Tailwind CSS watcher to automatically rebuild styles when you make changes: ```bash Spree CLI (Docker) theme={"theme":"night-owl"} spree rake spree:admin:tailwindcss:watch ``` ```bash Without Spree CLI theme={"theme":"night-owl"} bundle exec rake spree:admin:tailwindcss:watch ``` Or run the full development process, which includes the watcher when it's defined in `Procfile.dev`: ```bash Spree CLI (Docker) theme={"theme":"night-owl"} spree dev ``` ```bash Without Spree CLI theme={"theme":"night-owl"} bin/dev ``` The watcher monitors: * Your host app's CSS files in `app/assets/tailwind/` * The Spree Admin engine's CSS files * All files matching your `@source` patterns ### Building for Production CSS is automatically compiled during asset precompilation: ```bash theme={"theme":"night-owl"} bin/rails assets:precompile ``` ## Common Customization Examples ### Custom Primary Color Scheme ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} @theme { /* Use blue as the primary color */ --color-primary: #2563EB; } @layer components { /* Ensure buttons use the new primary color */ .btn-primary { @apply bg-blue-600 hover:bg-blue-700; } } ``` ### Dark Mode Adjustments ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} @layer base { /* Custom dark mode overrides */ .dark { --color-primary: #60A5FA; } } ``` ### Custom Form Styling ```css app/assets/tailwind/spree_admin.css theme={"theme":"night-owl"} @layer components { /* Custom input style */ .input-custom { @apply block w-full rounded-md border-zinc-300 shadow-sm; @apply focus:border-indigo-500 focus:ring-indigo-500; } /* Custom select style */ .select-custom { @apply block w-full rounded-md border-zinc-300 py-2 pl-3 pr-10; @apply focus:border-indigo-500 focus:outline-none focus:ring-indigo-500; } } ``` ## Troubleshooting ### Changes Not Appearing 1. Ensure the watcher is running: `bin/rails spree:admin:tailwindcss:watch` 2. Check that your files are in paths covered by `@source` directives 3. Hard refresh the browser (Cmd+Shift+R or Ctrl+Shift+R) ### Missing Utility Classes If a Tailwind class isn't working, ensure: 1. The class is used in a file that's scanned by `@source` 2. The class name is complete (not dynamically constructed) 3. Run a fresh build: `bin/rails spree:admin:tailwindcss:build` ### Listen Gem Required The watch task requires the `listen` gem. Add it to your Gemfile: ```ruby Gemfile theme={"theme":"night-owl"} group :development do gem 'listen', '>= 3.0' end ``` # Custom JavaScript for Admin Dashboard Source: https://spreecommerce.org/docs/developer/admin/custom-javascript Learn how to add custom JavaScript to your Spree Admin Dashboard ## Extending Admin Dashboard with JavaScript Spree Admin Dashboard can be easily extended with custom JavaScript. Most of the JavaScript in the Admin Dashboard is powered by a framework called [Stimulus.js](https://stimulus.hotwired.dev/). It's a very simple and minimalistic framework only enhancing our server-side rendered HTML with a bit of interactivity. ### 3rd party JavaScript libraries Spree Admin Dashboard comes with a few 3rd party JavaScript libraries already included. Main libraries we're using: * [Stimulus.js](https://stimulus.hotwired.dev/) * [Turbo](https://turbo.hotwired.dev/) * [EasyPick](https://easepick.com/) (date picker) * [TomSelect](https://tom-select.js.org/) (autocomplete/dropdowns) * [Sortable.js](https://github.com/SortableJS/Sortable) - sortable drag and drop lists * [Uppy](https://uppy.io/) - file uploader You can find them in the [app/assets/javascripts/vendor](https://github.com/spree/spree/blob/main/admin/vendor/javascript) directory. ### Managing JavaScript dependencies You're probably wondering why these libraries are in the `vendor` directory, and not in `node_modules`. That's because we're not using Node.js at all. So no Yarn or npm. We're using a different approach to manage dependencies. We're using a tool called [Importmaps](https://github.com/rails/importmap-rails) to manage dependencies. If you want to use a different JavaScript package manager you can do so by using [jsbundling-rails](https://github.com/rails/jsbundling-rails) gem. #### Install dependencies To install dependencies you need to run the following command: ```bash theme={"theme":"night-owl"} bin/importmap pin react ``` This will install the dependencies, download the files to your `vendor/javascript` directory and add them to your `config/importmap.rb` file, eg. ```ruby theme={"theme":"night-owl"} pin "react" # @19.1.0 ``` Now you can just import the library in your application entry point, eg. `application.js`: ```js theme={"theme":"night-owl"} import "react"; ``` ### Application entry point Mentioned above, the application entry point is the `application.js` file. It's located in the `app/javascript` directory. If you want to add custom JavaScript to your Spree Admin Dashboard, you can do so by adding a new file to the `app/javascript` directory. ### Stimulus Controllers Stimulus controllers are a way to add interactivity to your Spree Admin Dashboard. Admin Dashboard controllers can be found in the Spree repository in the [Admin Dashboard/app/javascript/spree/admin/controllers](https://github.com/spree/spree/tree/main/admin/app/javascript/spree/admin/controllers) directory. You can find more information about Stimulus controllers in the [Stimulus.js documentation](https://stimulus.hotwired.dev/handbook/controllers). To add a new controller you need to create a new file in the `app/javascript/controllers` directory. It will be automatically picked up by the application. ```js theme={"theme":"night-owl"} // app/javascript/controllers/hello_controller.js import { Controller } from '@hotwired/stimulus' export default class extends Controller { connect() { console.log("Hello Spree Commerce Stimulus!", this.element); } } ``` To use the controller in your HTML you need to add the `data-controller` attribute to your element, eg. ```html theme={"theme":"night-owl"}
Hello Spree Commerce Stimulus!
``` ## JavaScript snippets If you need to add a small piece of JavaScript code (eg. tracking, marketing, analytics, customer service etc.) you can do this by: 1. Declaring a new head partial in the `config/initializers/spree.rb` file, eg. ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.after_initialize do Spree.admin.partials.head << 'spree/admin/shared/my_tracking_code' end ``` ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.spree_admin.head_partials << 'spree/admin/shared/my_tracking_code' ``` 2. Creating a new file in the `app/views/spree/admin/shared/my_tracking_code.html.erb`, where you can add your tracking code, eg. ```erb theme={"theme":"night-owl"} ``` # Extending Admin Dashboard UI Source: https://spreecommerce.org/docs/developer/admin/extending-ui Inject custom partials into Spree Admin Dashboard pages using injection points to add fields, buttons, and sections without modifying core code. Spree Admin Dashboard allows you to easily extend existing pages and screens with your own code, without any need to modify the core codebase. This allows you to easily inject your custom UI elements without compromising the integrity of the core codebase. Which in effect allows you to safely update your Spree installation to the latest version. ## How it works The entire system works on the basis of injection points which are declared throughout the admin dashboard and allows you to push your own code there. Each injection point is identified by a key, eg. `body_end`. Let's say you want to add a new footer to the admin dashboard. You'll need to generate a template in your application: 1. Ensure you have the proper directory to store your templates: ```bash theme={"theme":"night-owl"} mkdir -p app/views/spree/admin/shared ``` 2. Create a new partial template file (partial templates file names start with underscore) ```bash theme={"theme":"night-owl"} touch app/views/spree/admin/shared/_additional_footer.html.erb ``` 3. Add your own code to the partial ```erb theme={"theme":"night-owl"}
Copyright <%= current_store.name %> <%= Time.current.year %>
``` 4. Register your partial in `config/initializers/spree.rb` ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.after_initialize do Spree.admin.partials.body_end << 'spree/admin/shared/additional_footer' end ``` ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.spree_admin.body_end << 'spree/admin/shared/additional_footer_partials' ``` For older versions of Spree you need to add `_partials` suffix for all injection points, eg. `body_end_partials` instead of `body_end`. The key is the name of the injection point, eg. `body_end`. Remember to use the correct path to your template file and skip the `_` prefix. 5. Restart your web server and you should see your new footer. Making further changes to the partial template will not require you to restart the web server. They will be picked up automatically. To summarize, we're injecting our code into the `body_end` array. This is a list of partials that will be rendered at the end of the body tag (before the closing `` tag). The order of the partials in the array is the order in which they will be rendered. ## Partials API The `Spree.admin.partials` object provides a clean API for registering partials to injection points. This API is available in Spree 5.2 and later. ### Accessing Available Injection Points You can list all available injection points programmatically: ```ruby theme={"theme":"night-owl"} # In Rails console or initializer Spree.admin.partials.keys # => ["head", "body_start", "body_end", "dashboard_analytics", "product_form", ...] ``` ### Registering Partials Each injection point is an array that you can append to: ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} # Add a single partial Spree.admin.partials.product_form << 'spree/admin/products/erp_section' # Add multiple partials Spree.admin.partials.dashboard_sidebar << 'spree/admin/dashboard/analytics_widget' Spree.admin.partials.dashboard_sidebar << 'spree/admin/dashboard/inventory_widget' ``` ### Viewing Registered Partials To see which partials are registered for a specific injection point: ```ruby theme={"theme":"night-owl"} Spree.admin.partials.product_form # => ["spree/admin/products/erp_section"] ``` ### Replacing All Partials You can also replace all partials for an injection point: ```ruby theme={"theme":"night-owl"} Spree.admin.partials.product_form = ['my/custom/partial'] ``` Replacing all partials will remove any partials registered by other extensions. Use this with caution. ## List of all injection points Here's a list of all places you can inject your custom code: ### Layout `head` Injects code into the `` tag `body_start` Injects code into the `` tag, before the main content `body_end` Injects code into the `` tag, after the main content ### Dashboard `dashboard_analytics` Injects code into the dashboard analytics section, eg. ```erb theme={"theme":"night-owl"}
Top ERP products
```
`dashboard_sidebar` Injects code into the dashboard sidebar, eg. ```erb theme={"theme":"night-owl"}
Latest ERP syncs
```
### Orders `orders_actions` Injects code into the page actions area (top right of the page) for the orders list page. This is useful for adding custom buttons, export options, or other actions. ```erb theme={"theme":"night-owl"} <%= link_to "Export to ERP", export_orders_to_erp_path, class: "btn btn-secondary" %> ``` `orders_header` Injects code between the page header and the main content area for the orders list page. This is useful for adding notifications, alerts, or additional information. ```erb theme={"theme":"night-owl"}
Processing: Orders are automatically processed and shipped within 24 hours.
```
`orders_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the orders list filters, to add custom filters. This partial has access to the `f` variable, which is the form builder for the filters, eg. ```erb theme={"theme":"night-owl"} <%= f.spree_text_field :q_number_cont, data: { filters_target: :input } %> ``` * `q_number_cont` - The name of the filter field. For filtering we're using [ransack](/docs/developer/core-concepts/search-filtering) gem, so the name of the filter field is the name of the attribute we're filtering by. * `data: { filters_target: :input }` - Needed for the [Stimulus Filters controller](https://github.com/spree/spree/blob/main/admin/app/javascript/spree/admin/controllers/filters_controller.js) to work. `order_page_dropdown` #### Variables The [Spree::Order](https://github.com/spree/spree/blob/main/core/app/models/spree/order.rb) object. Injects code into the order page dropdown. This partial has access to the `order` variable. To add an additional dropdown item, you can use the following code: ```erb theme={"theme":"night-owl"} <%= link_to "View Order in ERP", "https://erp.com/orders/#{order.number}", class: "dropdown-item", target: "_blank" %> ``` * `dropdown-item` is a CSS class for styling the dropdown item * `target: "_blank"` is used to open the link in a new tab `order_page_header` #### Variables The [Spree::Order](https://github.com/spree/spree/blob/main/core/app/models/spree/order.rb) object. Injects code into the order page header. To add an additional action button near the `...` button, you can use the following code: Injects code into the order page header. To add an additional action button near the `...` button, you can use the following code: ```erb theme={"theme":"night-owl"} <%= content_for :page_actions do %> <%= link_to "Send to ERP", "#", class: "btn btn-primary" %> <% end %> ``` To add an inline alert to the order page, you can use the following code: ```erb theme={"theme":"night-owl"} <%= content_for :page_alerts do %> <% if order.sent_to_erp_at.blank? %>
Order sent to ERP at <%= local_time(order.sent_to_erp_at) %>
<% else %>
Order not sent to ERP
<% end %> <% end %> ``` `local_time` is a helper for displaying the time in the user's timezone in a human readable **format** (based on the browser's timezone).
`order_page_body` #### Variables The [Spree::Order](https://github.com/spree/spree/blob/main/core/app/models/spree/order.rb) object. Injects code into the order page body. This partial has access to the `order` variable. To add a new section to the order page, you can use the following code: ```erb theme={"theme":"night-owl"}
ERP Integration
<% if order.sent_to_erp_at.blank? %>

Order not sent to ERP

<% else %>
  • ERP Order ID: <%= order.erp_order_id %>
  • Sent to ERP at: <%= local_time(order.sent_to_erp_at) %>
<% end %>
```
`order_page_sidebar` #### Variables The [Spree::Order](https://github.com/spree/spree/blob/main/core/app/models/spree/order.rb) object. Injects code into the order page sidebar. This partial has access to the `order` variable.
### Customers `users_actions` Injects code into the page actions area (top right of the page) for the customers list page. This is useful for adding custom buttons, export options, or other actions. ```erb theme={"theme":"night-owl"} <%= link_to "Sync with CRM", sync_customers_path, class: "btn btn-secondary" %> ``` `users_header` Injects code between the page header and the main content area for the customers list page. This is useful for adding notifications, alerts, or additional information. ```erb theme={"theme":"night-owl"}
Note: Customer data is synced with CRM every hour.
```
`users_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the customers list filters. This partial has access to the `f` variable, which is the form builder for the filters. ```erb theme={"theme":"night-owl"} <%= f.spree_select :custom_field_eq, [["VIP", "vip"], ["Regular", "regular"]], { include_blank: true }, { data: { filters_target: :input } } %> ``` * Use [ransack](/docs/developer/core-concepts/search-filtering) search syntax for filter field names * Include `data: { filters_target: :input }` for proper integration with the Stimulus Filters controller
### Stock Items `stock_items_actions` Injects code into the page actions area for the stock items list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export to WMS", export_stock_to_wms_path, class: "btn btn-secondary" %> ``` `stock_items_header` Injects code between the page header and the main content area for the stock items list page. ```erb theme={"theme":"night-owl"}
Warning: Low stock items require attention.
```
`stock_items_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the stock items list filters. ```erb theme={"theme":"night-owl"} <%= f.spree_number_field :count_on_hand_lt, label: "Stock Below", data: { filters_target: :input } %> ``` Use [ransack](/docs/developer/core-concepts/search-filtering) search syntax for filter field names (e.g., `_lt` for "less than").
### Admin Users `admin_users_actions` Injects code into the page actions area for the admin users list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export Users", export_admin_users_path, class: "btn btn-secondary" %> ``` `admin_users_header` Injects code between the page header and the main content area for the admin users list page. ```erb theme={"theme":"night-owl"}
Security: Review admin access regularly.
```
### Classifications `classifications_actions` Injects code into the page actions area for the classifications list page. ```erb theme={"theme":"night-owl"} <%= link_to "Bulk Update", bulk_update_classifications_path, class: "btn btn-secondary" %> ``` `classifications_header` Injects code between the page header and the main content area for the classifications list page. ```erb theme={"theme":"night-owl"}
Tip: Use drag and drop to reorder classifications.
```
### Coupon Codes `coupon_codes_actions` Injects code into the page actions area for the coupon codes list page. ```erb theme={"theme":"night-owl"} <%= link_to "Generate Bulk Codes", bulk_generate_coupons_path, class: "btn btn-secondary" %> ``` `coupon_codes_header` Injects code between the page header and the main content area for the coupon codes list page. ```erb theme={"theme":"night-owl"}
Notice: Expired codes will be automatically cleaned up.
```
### Custom Domains `custom_domains_actions` Injects code into the page actions area for the custom domains list page. ```erb theme={"theme":"night-owl"} <%= link_to "Verify All Domains", verify_all_domains_path, class: "btn btn-secondary" %> ``` `custom_domains_header` Injects code between the page header and the main content area for the custom domains list page. ```erb theme={"theme":"night-owl"}
SSL: SSL certificates are automatically managed.
```
### Customer Returns `customer_returns_actions` Injects code into the page actions area for the customer returns list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export Returns Report", export_returns_path, class: "btn btn-secondary" %> ``` `customer_returns_header` Injects code between the page header and the main content area for the customer returns list page. ```erb theme={"theme":"night-owl"}
Processing: Returns are processed within 3-5 business days.
```
`customer_returns_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the customer returns list filters. ```erb theme={"theme":"night-owl"} <%= f.spree_select :reason_eq, [["Damaged", "damaged"], ["Wrong Item", "wrong_item"]], { include_blank: true, label: "Return Reason" }, { data: { filters_target: :input } } %> ``` Use [ransack](/docs/developer/core-concepts/search-filtering) search syntax for filter field names (e.g., `_eq` for "equals").
### Digital Assets `digital_assets_actions` Injects code into the page actions area for the digital assets list page. ```erb theme={"theme":"night-owl"} <%= link_to "Bulk Upload", bulk_upload_assets_path, class: "btn btn-secondary" %> ``` `digital_assets_header` Injects code between the page header and the main content area for the digital assets list page. ```erb theme={"theme":"night-owl"}
Storage: Assets are stored in cloud storage with CDN.
```
### Exports `exports_actions` Injects code into the page actions area for the exports list page. ```erb theme={"theme":"night-owl"} <%= link_to "Schedule Export", new_scheduled_export_path, class: "btn btn-secondary" %> ``` `exports_header` Injects code between the page header and the main content area for the exports list page. ```erb theme={"theme":"night-owl"}
Retention: Export files are kept for 30 days.
```
### Gift Cards `gift_cards_actions` Injects code into the page actions area for the gift cards list page. ```erb theme={"theme":"night-owl"} <%= link_to "Bulk Generate", bulk_generate_gift_cards_path, class: "btn btn-secondary" %> ``` `gift_cards_header` Injects code between the page header and the main content area for the gift cards list page. ```erb theme={"theme":"night-owl"}
Security: Gift card codes are encrypted at rest.
```
`gift_cards_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the gift cards list filters. ```erb theme={"theme":"night-owl"} <%= f.spree_number_field :balance_gt, label: "Balance Greater Than", step: 0.01, data: { filters_target: :input } %> ``` Use [ransack](/docs/developer/core-concepts/search-filtering) search syntax for filter field names (e.g., `_gt` for "greater than").
### Integrations `integrations_actions` Injects code into the page actions area for the integrations list page. ```erb theme={"theme":"night-owl"} <%= link_to "Test All Connections", test_integrations_path, class: "btn btn-secondary" %> ``` `integrations_header` Injects code between the page header and the main content area for the integrations list page. ```erb theme={"theme":"night-owl"}
Status: Check integration health regularly.
```
### Invitations `invitations_actions` Injects code into the page actions area for the invitations list page. ```erb theme={"theme":"night-owl"} <%= link_to "Resend All Pending", resend_pending_invitations_path, class: "btn btn-secondary" %> ``` `invitations_header` Injects code between the page header and the main content area for the invitations list page. ```erb theme={"theme":"night-owl"}
Expiry: Invitations expire after 7 days.
```
### Option Types `option_types_actions` Injects code into the page actions area for the option types list page. ```erb theme={"theme":"night-owl"} <%= link_to "Import Options", import_option_types_path, class: "btn btn-secondary" %> ``` `option_types_header` Injects code between the page header and the main content area for the option types list page. ```erb theme={"theme":"night-owl"}
Variants: Option types are used to create product variants.
```
### Pages `pages_actions` Injects code into the page actions area for the pages list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export Content", export_pages_path, class: "btn btn-secondary" %> ``` `pages_header` Injects code between the page header and the main content area for the pages list page. ```erb theme={"theme":"night-owl"}
SEO: Remember to optimize page content for search engines.
```
### Payment Methods `payment_methods_actions` Injects code into the page actions area for the payment methods list page. ```erb theme={"theme":"night-owl"} <%= link_to "Test All Gateways", test_payment_gateways_path, class: "btn btn-secondary" %> ``` `payment_methods_header` Injects code between the page header and the main content area for the payment methods list page. ```erb theme={"theme":"night-owl"}
Configuration: Ensure all payment methods are properly configured.
```
### Promotions `promotions_actions` Injects code into the page actions area for the promotions list page. ```erb theme={"theme":"night-owl"} <%= link_to "Clone Selected", clone_promotions_path, class: "btn btn-secondary" %> ``` `promotions_header` Injects code between the page header and the main content area for the promotions list page. ```erb theme={"theme":"night-owl"}
Scheduling: Promotions can be scheduled to start and end automatically.
```
`promotions_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the promotions list filters. ```erb theme={"theme":"night-owl"} <%= f.spree_select :active_eq, [["Active", true], ["Inactive", false]], { include_blank: true, label: "Active Status" }, { data: { filters_target: :input } } %> ``` Use [ransack](/docs/developer/core-concepts/search-filtering) search syntax for filter field names.
### Properties `properties_actions` Injects code into the page actions area for the properties list page. ```erb theme={"theme":"night-owl"} <%= link_to "Import Properties", import_properties_path, class: "btn btn-secondary" %> ``` `properties_header` Injects code between the page header and the main content area for the properties list page. ```erb theme={"theme":"night-owl"}
Usage: Properties are used to add custom attributes to products.
```
### Refund Reasons `refund_reasons_actions` Injects code into the page actions area for the refund reasons list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export Reasons", export_refund_reasons_path, class: "btn btn-secondary" %> ``` `refund_reasons_header` Injects code between the page header and the main content area for the refund reasons list page. ```erb theme={"theme":"night-owl"}
Analytics: Track refund reasons to identify common issues.
```
### Reimbursement Types `reimbursement_types_actions` Injects code into the page actions area for the reimbursement types list page. ```erb theme={"theme":"night-owl"} <%= link_to "Configure Defaults", configure_reimbursement_defaults_path, class: "btn btn-secondary" %> ``` `reimbursement_types_header` Injects code between the page header and the main content area for the reimbursement types list page. ```erb theme={"theme":"night-owl"}
Processing: Define how customers are reimbursed for returns.
```
### Reports `reports_actions` Injects code into the page actions area for the reports list page. ```erb theme={"theme":"night-owl"} <%= link_to "Schedule Report", schedule_report_path, class: "btn btn-secondary" %> ``` `reports_header` Injects code between the page header and the main content area for the reports list page. ```erb theme={"theme":"night-owl"}
Automation: Reports can be scheduled to run automatically.
```
### Return Authorization Reasons `return_authorization_reasons_actions` Injects code into the page actions area for the return authorization reasons list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export Reasons", export_return_reasons_path, class: "btn btn-secondary" %> ``` `return_authorization_reasons_header` Injects code between the page header and the main content area for the return authorization reasons list page. ```erb theme={"theme":"night-owl"}
Policy: Define clear return reasons to streamline the process.
```
### Return Authorizations `return_authorizations_actions` Injects code into the page actions area for the return authorizations list page. ```erb theme={"theme":"night-owl"} <%= link_to "Bulk Process", bulk_process_returns_path, class: "btn btn-secondary" %> ``` `return_authorizations_header` Injects code between the page header and the main content area for the return authorizations list page. ```erb theme={"theme":"night-owl"}
Processing: Returns are processed in order of submission.
```
### Roles `roles_actions` Injects code into the page actions area for the roles list page. ```erb theme={"theme":"night-owl"} <%= link_to "Export Permissions", export_role_permissions_path, class: "btn btn-secondary" %> ``` `roles_header` Injects code between the page header and the main content area for the roles list page. ```erb theme={"theme":"night-owl"}
Security: Review role permissions regularly for security.
```
### Shipping Categories `shipping_categories_actions` Injects code into the page actions area for the shipping categories list page. ```erb theme={"theme":"night-owl"} <%= link_to "Calculate Rates", calculate_shipping_rates_path, class: "btn btn-secondary" %> ``` `shipping_categories_header` Injects code between the page header and the main content area for the shipping categories list page. ```erb theme={"theme":"night-owl"}
Organization: Use categories to group products with similar shipping requirements.
```
### Shipping Methods `shipping_methods_actions` Injects code into the page actions area for the shipping methods list page. ```erb theme={"theme":"night-owl"} <%= link_to "Test Integrations", test_shipping_integrations_path, class: "btn btn-secondary" %> ``` `shipping_methods_header` Injects code between the page header and the main content area for the shipping methods list page. ```erb theme={"theme":"night-owl"}
Configuration: Ensure shipping methods are properly configured for all zones.
```
### Stock Locations `stock_locations_actions` Injects code into the page actions area for the stock locations list page. ```erb theme={"theme":"night-owl"} <%= link_to "Sync Inventory", sync_all_locations_path, class: "btn btn-secondary" %> ``` `stock_locations_header` Injects code between the page header and the main content area for the stock locations list page. ```erb theme={"theme":"night-owl"}
Management: Stock locations help manage inventory across multiple warehouses.
```
### Stock Transfers `stock_transfers_actions` Injects code into the page actions area for the stock transfers list page. ```erb theme={"theme":"night-owl"} <%= link_to "Bulk Transfer", bulk_stock_transfer_path, class: "btn btn-secondary" %> ``` `stock_transfers_header` Injects code between the page header and the main content area for the stock transfers list page. ```erb theme={"theme":"night-owl"}
Tracking: All stock transfers are logged for audit purposes.
```
`stock_transfers_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the stock transfers list filters. ```erb theme={"theme":"night-owl"} <%= f.spree_select :status_eq, [["Pending", "pending"], ["Completed", "completed"]], { include_blank: true, label: "Transfer Status" }, { data: { filters_target: :input } } %> ``` Use [ransack](/docs/developer/core-concepts/search-filtering) search syntax for filter field names.
### Store Credit Categories `store_credit_categories_actions` Injects code into the page actions area for the store credit categories list page. ```erb theme={"theme":"night-owl"} <%= link_to "Set Default Category", set_default_credit_category_path, class: "btn btn-secondary" %> ``` `store_credit_categories_header` Injects code between the page header and the main content area for the store credit categories list page. ```erb theme={"theme":"night-owl"}
Organization: Use categories to organize different types of store credits.
```
### Store Credits `store_credits_actions` Injects code into the page actions area for the store credits list page. ```erb theme={"theme":"night-owl"} <%= link_to "Bulk Issue Credits", bulk_issue_credits_path, class: "btn btn-secondary" %> ``` `store_credits_header` Injects code between the page header and the main content area for the store credits list page. ```erb theme={"theme":"night-owl"}
Management: Store credits can be issued for returns, promotions, or customer service.
```
### Tax Categories `tax_categories_actions` Injects code into the page actions area for the tax categories list page. ```erb theme={"theme":"night-owl"} <%= link_to "Update Tax Rates", update_all_tax_rates_path, class: "btn btn-secondary" %> ``` `tax_categories_header` Injects code between the page header and the main content area for the tax categories list page. ```erb theme={"theme":"night-owl"}
Compliance: Ensure tax categories comply with local regulations.
```
### Tax Rates `tax_rates_actions` Injects code into the page actions area for the tax rates list page. ```erb theme={"theme":"night-owl"} <%= link_to "Import Tax Updates", import_tax_updates_path, class: "btn btn-secondary" %> ``` `tax_rates_header` Injects code between the page header and the main content area for the tax rates list page. ```erb theme={"theme":"night-owl"}
Updates: Tax rates should be reviewed and updated regularly.
```
### Taxonomies `taxonomies_actions` Injects code into the page actions area for the taxonomies list page. ```erb theme={"theme":"night-owl"} <%= link_to "Rebuild Tree", rebuild_taxonomy_tree_path, class: "btn btn-secondary" %> ``` `taxonomies_header` Injects code between the page header and the main content area for the taxonomies list page. ```erb theme={"theme":"night-owl"}
Navigation: Taxonomies are used to create product category navigation.
```
### Webhooks Subscribers `webhooks_subscribers_actions` Injects code into the page actions area for the webhooks subscribers list page. ```erb theme={"theme":"night-owl"} <%= link_to "Test All Webhooks", test_all_webhooks_path, class: "btn btn-secondary" %> ``` `webhooks_subscribers_header` Injects code between the page header and the main content area for the webhooks subscribers list page. ```erb theme={"theme":"night-owl"}
Monitoring: Monitor webhook delivery status and failures.
```
### Zones `zones_actions` Injects code into the page actions area for the zones list page. ```erb theme={"theme":"night-owl"} <%= link_to "Import Zones", import_zones_path, class: "btn btn-secondary" %> ``` `zones_header` Injects code between the page header and the main content area for the zones list page. ```erb theme={"theme":"night-owl"}
Geography: Zones define geographic regions for shipping and taxation.
```
### Products `products_actions` Injects code into the page actions area (top right of the page) for the products list page. This is useful for adding custom buttons, export options, or other actions. ```erb theme={"theme":"night-owl"} <%= link_to "Sync with PIM", sync_products_path, class: "btn btn-secondary" %> ``` `products_header` Injects code between the page header and the main content area for the products list page. This is useful for adding notifications, alerts, or additional information. ```erb theme={"theme":"night-owl"}
Inventory: Product inventory is synced with warehouse systems every 15 minutes.
```
`products_filters` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. Injects code into the products list filters. This partial has access to the `f` variable, which is the form builder for the filters. To add a new filter field, you can use the following code: ```erb theme={"theme":"night-owl"} <%= f.spree_text_field :q_name_cont, data: { filters_target: :input } %> ``` * `q_name_cont` is the name of the filter field. For filtering we're using [ransack](/docs/developer/core-concepts/search-filtering) gem, so the name of the filter field is the name of the attribute we're filtering by. * `data: { filters_target: :input }` is needed for the [Stimulus Filters controller](https://github.com/spree/spree/blob/main/admin/app/javascript/spree/admin/controllers/filters_controller.js) to work. `product_dropdown` Injects code into the products page dropdown. This partial has access to the `product` variable. To add an additional dropdown item, you can use the following code: ```erb theme={"theme":"night-owl"} <%= link_to "View Product in WMS", "https://wms.com/products/#{product.id}", class: "dropdown-item", target: "_blank" %> ``` Your code will be placed before the dropdown divider. `product_form` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. The [Spree::Product](https://github.com/spree/spree/blob/main/core/app/models/spree/product.rb) object. Injects code into the product form. This partial has access to the `f` variable, which is the form builder for the product form, and the `product` variable. To add a new section to the product form, you can use the following code: ```erb theme={"theme":"night-owl"}
ERP Integration
<%= f.spree_text_field :erp_product_id %>
``` The partial will be displayed for both new product form and edit product form. If you want to display the partial only on the edit product form, you can use the following code: ```erb theme={"theme":"night-owl"} <% if product.persisted? %> <%# ... your code ... %> <% end %> ``` And similarly for the new product form. ```erb theme={"theme":"night-owl"} <% if product.new_record? %> <%# ... your code ... %> <% end %> ```
`product_form_sidebar` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. The [Spree::Product](https://github.com/spree/spree/blob/main/core/app/models/spree/product.rb) object. Injects code into the product form sidebar. This partial has access to the `f` variable, which is the form builder for the product form, and the `product` variable. To add a new section to the product form sidebar, you can use the following code: ```erb theme={"theme":"night-owl"}
ERP Integration
<%= f.spree_text_field :erp_product_id %>
``` The partial will be displayed for both new product form and edit product form. If you want to display the partial only on the edit product form, you can use the following code: ```erb theme={"theme":"night-owl"} <% if product.persisted? %> <%# ... your code ... %> <% end %> ``` And similarly for the new product form. ```erb theme={"theme":"night-owl"} <% if product.new_record? %> <%# ... your code ... %> <% end %> ```
### Shipping Methods `shipping_method_form` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. The [Spree::ShippingMethod](https://github.com/spree/spree/blob/main/core/app/models/spree/shipping_method.rb) object. Injects code into the shipping method form. This partial has access to the `f` variable, which is the form builder for the shipping method form, and the `shipping_method` variable. To add a new section to the shipping method form, you can use the following code: ```erb theme={"theme":"night-owl"}
ERP Integration
<%= f.spree_text_field :erp_shipping_method_id %>
```
### Store Settings `store_form` #### Variables The [Spree::Admin::FormBuilder](/docs/developer/admin/form-builder) object. The [Spree::Store](https://github.com/spree/spree/blob/main/core/app/models/spree/store.rb) object. Injects code into the store settings form. This partial has access to the `f` variable, which is the form builder for the store form, and the `store` variable. To add a new section to the store form, you can use the following code: ```erb theme={"theme":"night-owl"}
ERP Integration
<%= f.spree_text_field :erp_store_id %>
```
## Related Documentation * [Admin Navigation](/docs/developer/admin/navigation) - Add custom menu items to the admin * [Admin Tables](/docs/developer/admin/tables) - Customize admin list views * [Form Builder](/docs/developer/admin/form-builder) - Build admin forms with Spree's form helpers * [Customization Quickstart](/docs/developer/customization/quickstart) - Overview of all customization options # Admin Form Builder API Source: https://spreecommerce.org/docs/developer/admin/form-builder Spree provides a custom `Spree::Admin::FormBuilder` that extends Rails' standard FormBuilder with additional helper methods designed specifically for the admin interface. This form builder ensures consistent styling, error handling, and behavior across all admin forms. ## Using the FormBuilder The FormBuilder is automatically available in admin forms when using `form_with`: ```erb theme={"theme":"night-owl"} <%= form_with model: [:admin, @product] do |f| %> <%= f.spree_text_field :name %> <%= f.spree_text_area :description %> <% end %> ``` ## Common Options All FormBuilder methods support these common options: | Option | Type | Description | | -------------- | ----------------- | -------------------------------------------------------------- | | `:label` | String or `false` | Custom label text, or `false` to hide the label entirely | | `:required` | Boolean | Adds a red asterisk (\*) indicator next to the label | | `:help` | String | Help text displayed below the field | | `:help_bubble` | String | Tooltip text displayed in a help bubble icon next to the label | | `:class` | String | Additional CSS classes for the input element | ## Text Input Methods ### spree\_text\_field Creates a text input field with Spree styling. ```erb theme={"theme":"night-owl"} <%= f.spree_text_field :name %> <%= f.spree_text_field :sku, required: true %> <%= f.spree_text_field :code, help: "Leave blank to auto-generate" %> <%= f.spree_text_field :slug, help_bubble: "URL-friendly identifier" %> ``` **Options:** * All standard Rails `text_field` options * All common FormBuilder options ### spree\_number\_field Creates a number input field with Spree styling. ```erb theme={"theme":"night-owl"} <%= f.spree_number_field :price, required: true, step: 0.01 %> <%= f.spree_number_field :quantity, min: 0 %> ``` **Options:** * All standard Rails `number_field` options (`:min`, `:max`, `:step`) * All common FormBuilder options ### spree\_money\_field Creates a locale-aware money/currency input field with automatic formatting. This field displays amounts in the currency's locale format (e.g., `1.234,56` for EUR, `1,234.56` for USD) and normalizes values to standard decimal format before form submission. ```erb theme={"theme":"night-owl"} <%= f.spree_money_field :amount, currency: 'USD' %> <%= f.spree_money_field :price, currency: 'EUR', required: true %> <%= f.spree_money_field :compare_at_amount, currency: @product.currency, label: false %> ``` **Options:** | Option | Type | Description | | ----------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `:currency` | String | Currency code (e.g., `'USD'`, `'EUR'`, `'PLN'`). Used to determine decimal/thousands separators and display the currency symbol | | `:value` | BigDecimal, Float, String | Override the displayed value (will be formatted for the currency's locale) | | `:prepend` | String | Text to prepend before the input field | | `:append` | String | Text to append after the input field (defaults to currency symbol when `:currency` is provided) | | `:disabled` | Boolean | Disable the input field | All common FormBuilder options (`:label`, `:required`, `:help`, `:help_bubble`) are also supported. **Features:** * **Locale-aware display:** Values are displayed using the currency's decimal mark (e.g., comma for EUR/PLN, dot for USD) * **Automatic normalization:** Values are converted to standard decimal format (with `.` as decimal separator) before form submission * **Currency symbol:** Automatically appends the currency symbol when `:currency` option is provided * **Mobile-friendly:** Uses `inputmode="decimal"` for appropriate mobile keyboard * **Auto-formatting:** Values are reformatted on blur to ensure consistent display **Example with nested form:** ```erb theme={"theme":"night-owl"} <%= f.fields_for :prices do |price_form| %> <%= price_form.spree_money_field :amount, currency: price_form.object.currency, disabled: !can?(:manage, price_form.object), label: false %> <% end %> ``` The money field uses a Stimulus controller (`money-field`) that handles formatting and normalization automatically. The decimal and thousands separators are determined by the currency's ISO standard configuration. ### spree\_email\_field Creates an email input field with Spree styling and HTML5 validation. ```erb theme={"theme":"night-owl"} <%= f.spree_email_field :email, required: true %> <%= f.spree_email_field :contact_email, help: "For customer support inquiries" %> ``` **Options:** * All standard Rails `email_field` options * All common FormBuilder options ## Date and Time Methods ### spree\_date\_field Creates a date picker input field. ```erb theme={"theme":"night-owl"} <%= f.spree_date_field :available_on %> <%= f.spree_date_field :discontinue_on, help: "Date when product will be discontinued" %> ``` **Options:** * All standard Rails `date_field` options (`:min`, `:max`) * All common FormBuilder options ### spree\_datetime\_field Creates a datetime picker input field. ```erb theme={"theme":"night-owl"} <%= f.spree_datetime_field :published_at %> <%= f.spree_datetime_field :sale_starts_at, required: true %> ``` **Options:** * All standard Rails `datetime_field` options * All common FormBuilder options ## Text Area Methods ### spree\_text\_area Creates a textarea with auto-grow functionality (expands as you type). ```erb theme={"theme":"night-owl"} <%= f.spree_text_area :description %> <%= f.spree_text_area :notes, rows: 10 %> <%= f.spree_text_area :meta_description, help: "Used for SEO, maximum 160 characters" %> ``` **Options:** * `:rows` - Number of visible rows (default: 5) * `:data` - Data attributes (default includes auto-grow Stimulus controller) * All standard Rails `text_area` options * All common FormBuilder options **Auto-grow behavior:** By default, the textarea automatically grows as the user types. This uses the `textarea-autogrow` Stimulus controller. ### spree\_rich\_text\_area Creates a rich text editor using Trix. ```erb theme={"theme":"night-owl"} <%= f.spree_rich_text_area :description %> <%= f.spree_rich_text_area :content, label: "Page Content" %> ``` **Features:** * WYSIWYG editing * Basic formatting (bold, italic, lists, links) * File attachments via Active Storage * All common FormBuilder options ## Select Methods ### spree\_select Creates a select dropdown field. ```erb theme={"theme":"night-owl"} <%= f.spree_select :status, ['draft', 'active', 'archived'], {}, {} %> <%= f.spree_select :tax_category_id, Spree::Category.pluck(:name, :id), { include_blank: 'Select Tax Category' } %> ``` **With autocomplete:** ```erb theme={"theme":"night-owl"} <%= f.spree_select :country_id, Spree::Country.pluck(:name, :id), { autocomplete: true } %> ``` **Parameters:** 1. `method` - The attribute name 2. `choices` - Array of options 3. `options` - Select options (`:include_blank`, `:prompt`, `:autocomplete`) 4. `html_options` - HTML attributes (`:class`, `:data`, `:disabled`) **Options:** * `:autocomplete` - Enables searchable dropdown with autocomplete functionality * All standard Rails `select` options * All common FormBuilder options ### spree\_collection\_select Creates a select dropdown from a collection of objects. ```erb theme={"theme":"night-owl"} <%= f.spree_collection_select :shipping_category_id, ShippingCategory.all, :id, :name, { include_blank: true }, {} %> <%= f.spree_collection_select :tax_category_id, TaxCategory.all, :id, :name, { autocomplete: true, required: true }, {} %> ``` **Parameters:** 1. `method` - The attribute name 2. `collection` - ActiveRecord collection or array of objects 3. `value_method` - Method to call for option value (e.g., `:id`) 4. `text_method` - Method to call for option text (e.g., `:name`) 5. `options` - Select options (`:include_blank`, `:autocomplete`) 6. `html_options` - HTML attributes **Options:** * `:autocomplete` - Enables searchable dropdown * All standard Rails `collection_select` options * All common FormBuilder options ## Checkbox and Radio Methods ### spree\_check\_box Creates a styled checkbox with custom controls. ```erb theme={"theme":"night-owl"} <%= f.spree_check_box :active %> <%= f.spree_check_box :featured, label: "Feature on homepage" %> <%= f.spree_check_box :accept_terms, required: true %> ``` **Options:** * All standard Rails `check_box` options * All common FormBuilder options **Styling:** Uses custom control classes for consistent appearance across the admin interface. ### spree\_radio\_button Creates a styled radio button with custom controls. ```erb theme={"theme":"night-owl"} <%= f.spree_radio_button :status, 'draft', id: 'status_draft' %> <%= f.spree_radio_button :status, 'published', id: 'status_published' %> ``` **Parameters:** 1. `method` - The attribute name 2. `tag_value` - The value for this radio option 3. `options` - Options hash (must include `:id`) **Options:** * `:id` - Required HTML ID for the radio button * All standard Rails `radio_button` options * All common FormBuilder options Radio buttons require unique IDs. Always specify the `:id` option. ## File Upload Methods ### spree\_file\_field Creates a file upload field with image preview, drag-and-drop support, and optional cropping functionality. ```erb theme={"theme":"night-owl"} <%= f.spree_file_field :image %> <%= f.spree_file_field :logo, width: 240, height: 240 %> <%= f.spree_file_field :avatar, crop: true %> <%= f.spree_file_field :featured_image, width: 1200, height: 600, crop: true %> ``` **Options:** | Option | Type | Default | Description | | --------------------- | ------- | ----------- | ------------------------------------------------------ | | `:width` | Integer | 300 | Width of the image preview in pixels | | `:height` | Integer | 300 | Height of the image preview in pixels | | `:crop` | Boolean | `false` | Enable image cropping with recommended size indicator | | `:auto_submit` | Boolean | `false` | Automatically submit form when file is selected | | `:can_delete` | Boolean | `true` | Show delete button for removing uploaded image | | `:css` | String | `''` | Additional CSS classes for the upload placeholder area | | `:allowed_file_types` | Array | Image types | Specify the allowed file types | All common FormBuilder options (`:label`, `:required`, `:help`, `:help_bubble`) are also supported. **Features:** * Drag-and-drop file upload * Image preview after upload * Optional image cropping with size recommendations * Delete button to remove uploaded files * Uses Active Storage direct upload **Example with all options:** ```erb theme={"theme":"night-owl"} <%= f.spree_file_field :hero_video_background, width: 1200, height: 630, crop: false, auto_submit: false, can_delete: true, help_bubble: "This video will loop in the background", label: 'Hero video background', allowed_file_types: %w[video/mp4 video/webm video/ogg] %> ``` ## Complete Form Example Here's a complete example showing various FormBuilder methods: ```erb theme={"theme":"night-owl"}
<%= Spree.t(:general_settings) %>
<%= f.spree_text_field :name, required: true, help: "Product name as displayed to customers" %> <%= f.spree_text_field :sku, label: "SKU", help_bubble: "Stock Keeping Unit - unique product identifier" %> <%= f.spree_number_field :price, required: true, step: 0.01, min: 0 %> <%= f.spree_rich_text_area :description, help: "Detailed product description with rich formatting" %> <%= f.spree_collection_select :tax_category_id, Spree::TaxCategory.all, :id, :name, { include_blank: 'None', required: true }, {} %> <%= f.spree_date_field :available_on, label: "Available Date", help: "Date when product becomes available for purchase" %> <%= f.spree_check_box :active, label: "Active" %>
``` ## Error Handling All FormBuilder methods automatically display validation errors below the field when present: ```erb theme={"theme":"night-owl"} <%= f.spree_text_field :name %> ``` If `@product.errors[:name]` contains errors, they will be displayed automatically in red text below the input field. ## Internationalization Labels are automatically translated using Rails I18n. The FormBuilder looks for translations in this order: 1. Custom label passed via `:label` option 2. `spree.{attribute_name}` key 3. `activerecord.attributes.spree/{model_name}.{attribute_name}` key Example translations: ```yaml theme={"theme":"night-owl"} # config/locales/en.yml en: spree: name: "Product Name" sku: "SKU Code" activerecord: attributes: spree/product: available_on: "Available On Date" discontinue_on: "Discontinuation Date" ``` ## Styling and CSS Classes All FormBuilder methods use consistent styling: * **Form groups:** Each field is wrapped in a `.form-group` div * **Input classes:** Text inputs use `.form-control` class * **Select classes:** Dropdowns use `.custom-select` class * **Checkboxes/Radios:** Use `.custom-control`, `.custom-checkbox`, `.custom-radio` classes You can add additional classes via the `:class` option: ```erb theme={"theme":"night-owl"} <%= f.spree_text_field :name, class: 'form-control-lg' %> ``` ## Best Practices **Use the Spree FormBuilder methods** instead of standard Rails helpers for consistent styling **Add `:required` option** to required fields for visual indication **Use `:help` text** to provide guidance for complex fields **Use `:help_bubble`** for additional context without cluttering the form **Enable autocomplete** on selects with many options (> 20 items) # Admin Dashboard Helper Methods Source: https://spreecommerce.org/docs/developer/admin/helper-methods Helper methods available in the Admin Dashboard for navigation, links, and utility functions. For UI components that render HTML elements (Dropdowns, Dialogs, Icons, etc.), see the [Components](/docs/developer/admin/components) documentation. ## Navigation Helpers ### `nav_item` Creates a navigation item with optional icon. ```erb theme={"theme":"night-owl"} <%= nav_item 'Dashboard', spree.admin_dashboard_path %> <%= nav_item 'Products', spree.admin_products_path, icon: 'package' %> <%= nav_item 'Orders', spree.admin_orders_path, active: true %> ``` With a block for custom content: ```erb theme={"theme":"night-owl"} <%= nav_item spree.admin_products_path do %> Products New <% end %> ``` | Option | Type | Description | | -------- | ------- | ------------------------------- | | `label` | String | The text to display | | `url` | String | The URL for the link (required) | | `icon` | String | Icon name to prepend | | `active` | Boolean | Force active state | | `data` | Hash | Data attributes | ### `link_to_with_icon` Creates a link with an icon. ```erb theme={"theme":"night-owl"} <%= link_to_with_icon 'eye', "View Order", spree.admin_order_path(order), class: "btn btn-primary" %> <%= link_to_with_icon 'pencil', "Edit", edit_path, class: "dropdown-item" %> <%= link_to_with_icon 'trash', "Delete", delete_path, no_text: true, title: "Delete item" %> ``` | Parameter | Type | Description | | ----------- | ------- | ------------------------------------------------------ | | `icon_name` | String | Icon name from [Tabler Icons](https://tabler.io/icons) | | `text` | String | Link text | | `url` | String | Link URL (use `spree.` routes for internal links) | | `no_text` | Boolean | Show only icon with text as tooltip | | `title` | String | Tooltip text | ### `active_link_to_with_icon` Same as `link_to_with_icon`, but adds the `active` class if the current page matches the URL. ```erb theme={"theme":"night-owl"} <%= active_link_to_with_icon 'package', "Products", spree.admin_products_path, class: "nav-link" %> ``` ### `link_to_edit` Renders an edit button for a resource. Only renders if the user has `update` permission. ```erb theme={"theme":"night-owl"} <%= link_to_edit(@product) %> <%= link_to_edit(@product, url: custom_edit_path(@product)) %> <%= link_to_edit(@order, class: 'btn btn-primary') %> ``` | Option | Type | Default | Description | | ------- | ------ | ---------------------- | ---------------------------------------------------- | | `url` | String | auto | Custom URL (defaults to `edit_object_url(resource)`) | | `class` | String | `btn btn-light btn-sm` | CSS classes | | `data` | Hash | `{action: 'edit'}` | Data attributes | ### `link_to_delete` Renders a delete button with confirmation. Only renders if the user has `destroy` permission. ```erb theme={"theme":"night-owl"} <%= link_to_delete(@product) %> <%= link_to_delete(@product, name: 'Remove') %> <%= link_to_delete(@product, no_text: true) %> <%= link_to_delete(@product, icon: 'x') %> ``` | Option | Type | Default | Description | | --------- | ------- | ----------------------- | ----------------------------------------------- | | `url` | String | auto | Custom URL (defaults to `object_url(resource)`) | | `name` | String | "Delete" | Button text | | `icon` | String | `trash` | Custom icon | | `no_text` | Boolean | false | Show only icon | | `class` | String | `btn btn-danger btn-sm` | CSS classes | | `data` | Hash | confirm + method | Data attributes | ### `button` Renders a submit button with optional icon and loading state. ```erb theme={"theme":"night-owl"} <%= button(Spree.t('actions.save')) %> <%= button(Spree.t('actions.save'), 'device-floppy') %> <%= button('Submit', 'send', 'submit', class: 'btn-success') %> ``` | Parameter | Type | Default | Description | | ------------- | ------ | ------------- | ------------- | | `text` | String | required | Button text | | `icon_name` | String | nil | Optional icon | | `button_type` | String | `submit` | Button type | | `class` | String | `btn-primary` | CSS classes | ### `external_link_to` Renders an external link that opens in a new tab with an indicator icon. ```erb theme={"theme":"night-owl"} <%= external_link_to 'Documentation', 'https://docs.spreecommerce.org' %> <%= external_link_to 'GitHub', 'https://github.com/spree/spree' %> ``` With a block: ```erb theme={"theme":"night-owl"} <%= external_link_to 'https://example.com' do %> Custom content <% end %> ``` | Parameter | Type | Description | | --------- | ------ | -------------------- | | `label` | String | Link text | | `url` | String | External URL | | `target` | Symbol | Default: `:blank` | | `rel` | Symbol | Default: `:nofollow` | ### `external_page_preview_link` Renders a link to preview a resource on the storefront. ```erb theme={"theme":"night-owl"} <%= external_page_preview_link(@product) %> <%= external_page_preview_link(@post) %> ``` ### `page_header_back_button` Renders a back button for page headers with smart return URL handling. ```erb theme={"theme":"night-owl"} <%= page_header_back_button(spree.admin_products_path) %> <%= page_header_back_button(spree.admin_products_path, @product) %> <%= page_header_back_button(spree.admin_orders_path, @order, 'Back to Orders') %> ``` | Parameter | Type | Description | | ------------- | ------ | --------------------------------------------------------- | | `default_url` | String | Default URL to navigate back to | | `object` | Object | Optional object to check for stored return URL in session | | `label` | String | Optional label text | ### `per_page_dropdown` Renders a dropdown for selecting items per page on index pages. ```erb theme={"theme":"night-owl"} <%= per_page_dropdown %> ``` Automatically detects the resource type and provides appropriate options based on configuration. ### `link_to_export_modal` Renders a button to open the export modal. Only renders if user can create exports. ```erb theme={"theme":"night-owl"} <%= link_to_export_modal %> ``` ## Store Helpers Helpers for store-specific options and unit systems. ### `weight_units` Returns weight unit options based on the store's unit system. ```erb theme={"theme":"night-owl"} <%= f.select :weight_unit, weight_units %> ``` **Returns:** * Metric: `[["Kilogram", "kg"], ["Gram", "g"]]` * Imperial: `[["Pound", "lb"], ["Ounce", "oz"]]` | Parameter | Type | Default | Description | | --------- | ------------ | --------------- | -------------------------- | | `store` | Spree::Store | `current_store` | Store to check unit system | ### `dimension_units` Returns dimension unit options based on the store's unit system. ```erb theme={"theme":"night-owl"} <%= f.select :dimension_unit, dimension_units %> ``` **Returns:** * Metric: `[["Centimeter", "cm"], ["Millimeter", "mm"]]` * Imperial: `[["Inch", "in"], ["Foot", "ft"]]` | Parameter | Type | Default | Description | | --------- | ------------ | --------------- | -------------------------- | | `store` | Spree::Store | `current_store` | Store to check unit system | ### `unit_systems` Returns available unit system options. ```erb theme={"theme":"night-owl"} <%= f.select :unit_system, unit_systems %> ``` **Returns:** `[["Metric System", "metric"], ["Imperial System", "imperial"]]` ### `display_on_options` Returns display location options for calculators and payment/shipping methods. ```erb theme={"theme":"night-owl"} <%= f.select :display_on, display_on_options %> ``` **Returns:** `[["Both", "both"], ["Backend", "back_end"], ["Frontend", "front_end"]]` ## Turbo Helpers Helpers for Turbo Streams and Turbo-enhanced forms. ### `turbo_close_dialog` Returns a Turbo Stream response that closes the main dialog. ```erb theme={"theme":"night-owl"} <%# In a turbo_stream.erb response %> <%= turbo_close_dialog %> ``` ### `turbo_render_alerts` Returns a Turbo Stream response that updates the alerts frame. ```erb theme={"theme":"night-owl"} <%= turbo_render_alerts %> <%= turbo_render_alerts(:custom_alerts) %> ``` | Parameter | Type | Default | Description | | ------------ | ------ | --------- | --------------------- | | `frame_name` | Symbol | `:alerts` | Turbo frame to update | ### `turbo_save_button_tag` Creates a save button with loading state for Turbo forms. ```erb theme={"theme":"night-owl"} <%= turbo_save_button_tag %> <%= turbo_save_button_tag('Update Product') %> <%= turbo_save_button_tag('Save', class: 'btn btn-success') %> ``` **Features:** * Shows spinner during submission * Integrates with `turbo-submit-button` Stimulus controller * Prevents double submissions | Parameter | Type | Default | Description | | --------- | ------ | ----------------------------- | ------------ | | `label` | String | "Save" | Button label | | `class` | String | `btn btn-primary text-center` | CSS classes | ## Context Helpers ### `current_store` Returns the current store instance. Store name Example: "My Store" Store URL Example: `https://mystore.com` Unique store identifier Example: `my-store` Email address used for sending emails Example: `store@example.com` Default store currency Example: `USD` List of supported currencies Example: `USD,EUR,GBP` Default store locale Example: `en` List of supported locales Example: `en,es,fr` Default country ID Example: `1` Default country Country ID Example: 1 Country name Example: `United States` Country ISO3 code Example: `USA` Country ISO code Example: `US` Country ISO name Example: `UNITED STATES` Whether states are required for this country Example: `true` Whether zipcodes are required for this country Example: `true` Checkout zone ID Example: `2` SEO title Example: `My Amazing Store` Meta description for SEO Example: `The best products at the best prices` ```erb theme={"theme":"night-owl"} <%= current_store.name %> <%= current_store.default_currency %> ``` ### `current_currency` Returns the currently selected currency for the admin session. ```erb theme={"theme":"night-owl"} <%= current_currency %> <%# => "USD" %> ``` ### `current_vendor` Available only in [Spree Enterprise Edition](https://spreecommerce.com/pricing). Unique vendor identifier Example: `1` Vendor name Example: "My Vendor" Vendor state/status Example: `active` ### `supported_currencies` Returns the list of supported currencies for the current store. ```erb theme={"theme":"night-owl"} <%= supported_currencies %> <%# => ["USD", "EUR", "GBP"] %> ``` ### `try_spree_current_user` Returns the current user object or `nil` if not signed in. ```erb theme={"theme":"night-owl"} <%= try_spree_current_user&.email %> <% if try_spree_current_user.present? %> Signed in as <%= try_spree_current_user.email %> <% end %> ``` ### `available_countries_iso` Returns ISO codes for countries available for checkout in the current store. ```erb theme={"theme":"night-owl"} <%= available_countries_iso %> <%# => ["US", "CA", "GB"] %> ``` ## Utility Helpers ### `flag_emoji` Returns the flag emoji for a country ISO code. ```erb theme={"theme":"night-owl"} <%= flag_emoji('US') %> <%# => 🇺🇸 %> <%= flag_emoji('GB') %> <%# => 🇬🇧 %> <%= flag_emoji('JP') %> <%# => 🇯🇵 %> ``` ### `required_span_tag` Renders a red asterisk indicator for required fields. ```erb theme={"theme":"night-owl"} <%= label_tag :name %> <%= required_span_tag %> ``` **Renders:** ` *` ### `error_message_on` Renders validation error messages for a form field. ```erb theme={"theme":"night-owl"} <%= error_message_on(@product, :name) %> <%= error_message_on(@order, :email) %> ``` **Renders:** `can't be blank` ### `settings_area?` Returns `true` if the current page is in the settings area. ```erb theme={"theme":"night-owl"} <% if settings_area? %> <%= render 'spree/admin/shared/settings_sidebar' %> <% end %> ``` ### `enterprise_edition?` Returns `true` if Spree Enterprise Edition is installed. ```erb theme={"theme":"night-owl"} <% if enterprise_edition? %> <%= render 'spree/admin/vendors/selector' %> <% end %> ``` ### `allowed_file_types_for_upload` Returns allowed file types for Active Storage uploads. ```erb theme={"theme":"night-owl"} <%= allowed_file_types_for_upload %> <%# => ["image/png", "image/jpeg", "image/gif", "image/webp"] %> ``` ### `render_admin_partials` Renders all registered partials for an injection point. ```erb theme={"theme":"night-owl"} <%= render_admin_partials(:product_form, f: f, product: @product) %> <%= render_admin_partials(:order_page_sidebar, order: @order) %> ``` See [Extending UI](/docs/developer/admin/extending-ui) for more information about injection points. ## Preference Helpers Helpers for rendering preference fields in settings forms. ### `preference_fields` Renders all preference fields for an object. ```erb theme={"theme":"night-owl"} <%= preference_fields(@payment_method, f) %> <%= preference_fields(@calculator, f, i18n_scope: 'spree.calculator') %> ``` ### `preference_field` Renders a single preference field. ```erb theme={"theme":"night-owl"} <%= preference_field(@payment_method, f, :api_key) %> ``` ### `preference_field_for` Renders a form field based on preference type. ```erb theme={"theme":"night-owl"} <%= preference_field_for(f, :preferred_api_key, type: :string) %> <%= preference_field_for(f, :preferred_test_mode, type: :boolean) %> <%= preference_field_for(f, :preferred_amount, type: :decimal, step: 0.01) %> ``` | Type | Rendered Field | | ----------- | ---------------------- | | `:integer` | Number field | | `:decimal` | Number field with step | | `:boolean` | Checkbox | | `:string` | Text field | | `:password` | Password field | | `:text` | Text area | # Extending Admin Navigation Source: https://spreecommerce.org/docs/developer/admin/navigation Spree Admin Dashboard provides a flexible navigation system that allows you to easily extend the sidebar navigation with your own menu items without modifying the core codebase. This enables safe updates while maintaining your customizations. Starting with Spree 5.2, the navigation system uses a declarative API accessible via `Spree.admin.navigation`. This allows you to programmatically add, modify, and remove navigation items directly in your initializers. ## Basic Usage Add navigation items in your `config/initializers/spree.rb` file: ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.after_initialize do sidebar_nav = Spree.admin.navigation.sidebar sidebar_nav.add :brands, label: :brands, url: :admin_brands_path, icon: 'award', position: 35, active: -> { controller_name == 'brands' }, if: -> { can?(:manage, Spree::Brand) } end ``` ## Available Options All navigation items support the following options: The text label for the navigation item. Can be a symbol (translation key using `Spree.t`) or a string. The URL for the navigation item. Can be a route helper symbol (`:admin_brands_path`) or a lambda returning a URL. Icon name from [Tabler Icons](https://tabler.io/icons). Numeric position in the menu. Lower numbers appear first. Lambda to determine if the link should be highlighted as active. Receives view context. Conditional display logic. The item only appears if this lambda returns true. Has access to view context helpers like `can?`, `current_store`, etc. Badge text/count to display next to the label. Can be a string or lambda that returns a value. CSS class for badge styling (e.g., `'badge-info'`, `'badge-warning'`). Tooltip text shown on hover. Link target attribute (e.g., `'_blank'` to open in a new tab). Creates a section divider with the given label instead of a clickable link. The key of an existing navigation item to nest this item under. This is the simplest way to add items to existing submenus. ## Navigation Contexts The navigation system supports multiple contexts. Spree provides predefined contexts for common use cases, and you can register custom contexts for your specific needs. ### Sidebar Navigation The main sidebar navigation: ```ruby theme={"theme":"night-owl"} sidebar_nav = Spree.admin.navigation.sidebar ``` ### Settings Navigation Navigation in the Settings area: ```ruby theme={"theme":"night-owl"} settings_nav = Spree.admin.navigation.settings ``` ### Page Tab Navigation Spree provides several predefined tab contexts for common admin pages: ```ruby theme={"theme":"night-owl"} tax_tabs = Spree.admin.navigation.tax_tabs shipping_tabs = Spree.admin.navigation.shipping_tabs team_tabs = Spree.admin.navigation.team_tabs stock_tabs = Spree.admin.navigation.stock_tabs returns_tabs = Spree.admin.navigation.returns_tabs developers_tabs = Spree.admin.navigation.developers_tabs audit_tabs = Spree.admin.navigation.audit_tabs ``` ### Registering Contexts Use `register_context` to create a new navigation context: ```ruby theme={"theme":"night-owl"} # Returns a Spree::Admin::Navigation instance custom_tabs = Spree.admin.navigation.register_context(:custom_tabs) ``` The unique name for the navigation context. Will be converted to a symbol internally. **Returns:** `Spree::Admin::Navigation` - The navigation context instance **Note:** Calling `register_context` multiple times with the same name returns the same instance (idempotent). ### Custom Tab Contexts You can create custom tab contexts for your own admin pages using `register_context`: ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.after_initialize do # Register custom tab navigation for your brands page brand_tabs = Spree.admin.navigation.register_context(:brand_tabs) brand_tabs.add :active_brands, label: 'Active Brands', url: -> { spree.admin_brands_path(status: 'active') }, position: 10, active: -> { params[:status] == 'active' } brand_tabs.add :archived_brands, label: 'Archived Brands', url: -> { spree.admin_brands_path(status: 'archived') }, position: 20, active: -> { params[:status] == 'archived' } end ``` Then render the tabs in your view using the `render_tab_navigation` helper: ```erb app/views/spree/admin/brands/index.html.erb theme={"theme":"night-owl"} <%= render_tab_navigation(:brand_tabs) %> ``` Always register custom contexts in your initializer before accessing them. Attempting to access an unregistered context will raise a `NoMethodError`. ### Listing All Contexts You can list all registered navigation contexts: ```ruby theme={"theme":"night-owl"} # Returns an array of context names (symbols) Spree.admin.navigation.contexts # => [:sidebar, :settings, :brand_tabs, :inventory_tabs] ``` ### Checking If a Context Exists ```ruby theme={"theme":"night-owl"} # Check if a context has been created Spree.admin.navigation.context?(:brand_tabs) # => true or false ``` ## Creating Submenus Add a parent item, then add child items using the `parent` option: ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} sidebar_nav.add :brands, label: :brands, url: :admin_brands_path, icon: 'award', position: 35, if: -> { can?(:manage, Spree::Brand) } sidebar_nav.add :all_brands, label: 'All Brands', url: :admin_brands_path, position: 10, parent: :brands, active: -> { controller_name == 'brands' } sidebar_nav.add :brand_categories, label: 'Brand Categories', url: :admin_brand_categories_path, position: 20, parent: :brands, active: -> { controller_name == 'brand_categories' }, if: -> { can?(:manage, Spree::BrandCategory) } ``` Parent items are automatically marked as active when any of their children are active. You don't need to manually define the `active` option for parent items. ## Modifying Existing Navigation ### Finding Navigation Items ```ruby theme={"theme":"night-owl"} sidebar_nav = Spree.admin.navigation.sidebar products_nav = sidebar_nav.find(:products) ``` ### Adding to Existing Submenus Use the `parent` option to add an item to an existing submenu: ```ruby theme={"theme":"night-owl"} sidebar_nav.add :brands, label: :brands, url: :admin_brands_path, position: 50, parent: :products, active: -> { controller_name == 'brands' }, if: -> { can?(:manage, Spree::Brand) } ``` ### Removing Navigation Items ```ruby theme={"theme":"night-owl"} sidebar_nav.remove(:vendors) ``` ### Updating Navigation Items ```ruby theme={"theme":"night-owl"} sidebar_nav.update(:products, label: 'Catalog', icon: 'shopping-cart') ``` ### Replacing Navigation Items ```ruby theme={"theme":"night-owl"} sidebar_nav.replace(:products, label: 'Products', icon: 'package') do |products| # Define new submenu structure end ``` ### Moving Navigation Items ```ruby theme={"theme":"night-owl"} # Move to specific position sidebar_nav.move(:brands, position: 25) # Move before another item sidebar_nav.move(:brands, before: :products) # Move after another item sidebar_nav.move(:brands, after: :products) # Move to first position sidebar_nav.move(:brands, position: :first) # Move to last position sidebar_nav.move(:brands, position: :last) ``` ## Advanced Examples ### Navigation with Dynamic Badge ```ruby theme={"theme":"night-owl"} sidebar_nav.add :orders, label: :orders, url: :admin_orders_path, icon: 'inbox', position: 20, active: -> { controller_name == 'orders' }, if: -> { can?(:manage, Spree::Order) }, badge: -> { count = Spree::Order.ready_to_ship.count count if count.positive? }, badge_class: 'badge-warning' ``` ### Section Dividers ```ruby theme={"theme":"night-owl"} sidebar_nav.add :settings_section, section_label: 'Settings', position: 90 ``` ### Dynamic URLs ```ruby theme={"theme":"night-owl"} sidebar_nav.add :store_settings, label: :settings, url: -> { spree.edit_admin_store_path(section: 'general-settings') }, icon: 'settings', position: 100 ``` ### Complex Conditional Display ```ruby theme={"theme":"night-owl"} sidebar_nav.add :vendors, label: :vendors, url: 'https://spreecommerce.org/marketplace-ecommerce/', icon: 'heart-handshake', position: 35, if: -> { can?(:manage, current_store) && !defined?(SpreeEnterprise) }, badge: 'Enterprise', tooltip: 'Multi-Vendor Marketplace is available in the Enterprise Edition', target: '_blank' ``` ### Complex Active State Logic ```ruby theme={"theme":"night-owl"} sidebar_nav.add :products, label: :products, url: :admin_products_path, icon: 'package', position: 30, active: -> { %w[products external_categories taxons taxonomies option_types option_values properties stock_items stock_transfers variants digital_assets].include?(controller_name) }, if: -> { can?(:manage, Spree::Product) } ``` ## Best Practices Always use `if: -> { can?(...) }` to ensure users only see navigation items they have permission to access. Use symbols for labels (e.g., `label: :brands`) to support internationalization via `Spree.t`. Define clear active state logic using lambdas to highlight the current section properly. Use consistent position intervals (e.g., 10, 20, 30) to leave room for future additions. ### Common Positioning Reference Main sidebar navigation positions: * Getting Started: 5 * Home: 10 * Orders: 20 * Returns: 25 * Products: 30 * Customers: 40 * Promotions: 50 * Reports: 60 * Storefront: 70 * Integrations: 80 * Settings Section: 90 * Settings: 100 * Admin Users: 110 ### Troubleshooting * Restart your server after modifying initializers * Check authorization: ensure `if: -> { can?(...) }` returns true * Verify the item isn't hidden by a parent's `if` condition * Ensure `active:` lambda returns true/false * Check that controller\_name or other conditions match correctly * For submenus, ensure parent uses same active logic as children * Ensure the badge lambda returns a non-nil value * Check that the badge value is truthy (empty strings won't display) * For numeric badges, ensure the count is greater than 0 *** ## Previous versions The following documentation applies to Spree 5.1 and earlier. If you're using Spree 5.2+, please refer to the documentation above. ### How it works The admin navigation system works through injection points defined throughout the sidebar. You can inject custom navigation items into these predefined locations, add new top-level menu items, or create nested submenus. ### Navigation Injection Points The main navigation file is located at `admin/app/views/spree/admin/shared/sidebar/_store_nav.html.erb` and provides several injection points: #### Available Injection Points `store_nav_partials` Injects navigation items into the main sidebar navigation, after the Reports item and before the Storefront and Integrations sections. Main navigation injection point This is the primary injection point for adding custom top-level navigation items. `store_products_nav_partials` Injects navigation items into the Products submenu, after the Properties item. Use this to add product-related navigation items that logically belong under the Products section. `store_orders_nav_partials` Injects navigation items into the Orders submenu, after the Draft Orders item. Use this to add order-related navigation items. `store_settings_nav_partials` Injects navigation items into the Settings section, after the Policies item. Use this when Settings mode is active to add configuration-related items. `settings_nav_partials` Injects navigation items at the end of the Settings section. Use this to add additional settings-related navigation items. ### Using the nav\_item Helper The `nav_item` helper method is provided by `Spree::Admin::NavigationHelper` and makes it easy to create properly formatted navigation items. ### Method Signature ```ruby theme={"theme":"night-owl"} nav_item(label = nil, url, icon: nil, active: nil, data: {}) ``` #### Parameters The text label for the navigation item. Can be HTML-safe content. The URL the navigation item links to. Use the `spree.` route helper prefix. Optional icon name from [Tabler Icons](https://tabler.io/icons). The icon will be displayed before the label. Manually set whether the link should be marked as active. If not specified, it will be auto-detected based on the current URL. Additional data attributes to add to the link element. #### Basic Usage ```erb theme={"theme":"night-owl"} <%= nav_item(Spree.t(:custom_section), spree.admin_custom_path, icon: 'star') %> ``` #### With Active State ```erb theme={"theme":"night-owl"} <%= nav_item( Spree.t(:inventory), spree.admin_inventory_path, icon: 'boxes', active: controller_name == 'inventory' ) %> ``` #### With Block Content ```erb theme={"theme":"night-owl"} <%= nav_item(nil, spree.admin_dashboard_path, icon: 'home') do %> <%= icon 'home' %> <%= Spree.t(:dashboard) %> New <% end %> ``` ### Adding a Simple Navigation Item Let's add a new "Inventory" navigation item to the main sidebar. #### Step 1: Create the Partial ```bash theme={"theme":"night-owl"} mkdir -p app/views/spree/admin/shared touch app/views/spree/admin/shared/_inventory_nav.html.erb ``` #### Step 2: Add Navigation Code ```erb app/views/spree/admin/shared/_inventory_nav.html.erb theme={"theme":"night-owl"} <% if can?(:manage, Spree::Inventory) %> <%= nav_item( Spree.t(:inventory), spree.admin_inventory_index_path, icon: 'boxes', active: controller_name == 'inventory' ) %> <% end %> ``` Always wrap your navigation items with authorization checks using `can?()` to ensure users only see menu items they have permission to access. #### Step 3: Register the Partial Add this to your `config/initializers/spree.rb`: ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.after_initialize do Spree.admin.navigation.store << 'spree/admin/shared/inventory_nav' end ``` ```ruby config/initializers/spree.rb theme={"theme":"night-owl"} Rails.application.config.spree_admin.store_nav_partials << 'spree/admin/shared/inventory_nav' ``` #### Step 4: Add Translations In your `config/locales/en.yml`: ```yaml config/locales/en.yml theme={"theme":"night-owl"} en: spree: inventory: "Inventory" ``` #### Step 5: Restart Your Server Restart your web server to load the initializer changes. The navigation item should now appear in the sidebar. ### Creating Nested Navigation (Submenus) To create a navigation item with a submenu, you need to use the `nav-submenu` class and manage the visibility based on the active state. #### Example: Adding a Nested Menu ```erb theme={"theme":"night-owl"} <% inventory_active = %w[inventory warehouses stock_movements].include?(controller_name) %> <% if can?(:manage, Spree::Inventory) %> <%= nav_item( Spree.t(:inventory), spree.admin_inventory_index_path, icon: 'boxes', active: inventory_active ) %> <% end %> ``` #### Key Points for Submenus 1. **Active State Variable**: Define a variable to track when any item in the menu group is active: ```erb theme={"theme":"night-owl"} <% inventory_active = %w[inventory warehouses stock_movements].include?(controller_name) %> ``` 2. **Parent Navigation Item**: Use the active state variable for the parent item: ```erb theme={"theme":"night-owl"} <%= nav_item(..., active: inventory_active) %> ``` 3. **Submenu Container**: Use the `nav-submenu` class and conditionally add `d-none` to hide when inactive: ```erb theme={"theme":"night-owl"}