Skip to main content

Documentation Index

Fetch the complete documentation index at: https://spreecommerce.org/docs/llms.txt

Use this file to discover all available pages before exploring further.

The Admin API uses 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:
const orders = await client.orders.list({
  status_eq: 'complete',
  total_gteq: 100,
  email_cont: '@example.com',
})
The SDK automatically wraps filter keys in q[...] and appends [] for array values — pass flat params.

Common predicates

PredicateDescriptionSDKcURL
eqEqualsstatus_eq: 'complete'q[status_eq]=complete
not_eqNot equalsstatus_not_eq: 'canceled'q[status_not_eq]=canceled
contContains (case-insensitive)email_cont: '@acme'q[email_cont]=@acme
startStarts withnumber_start: 'R10'q[number_start]=R10
endEnds withslug_end: 'sale'q[slug_end]=sale
lt / lteqLess than / less than or equaltotal_lteq: 50q[total_lteq]=50
gt / gteqGreater than / greater than or equaltotal_gteq: 100q[total_gteq]=100
inIn a liststatus_in: ['complete', 'canceled']q[status_in][]=complete&q[status_in][]=canceled
null / not_nullIs null / not nullcompleted_at_not_null: trueq[completed_at_not_null]=true
true / falseBooleanaccepts_email_marketing_true: 1q[accepts_email_marketing_true]=1

Prefixed IDs in filters

Resource ID filters accept Stripe-style prefixed IDs directly. The server decodes them before querying:
// All orders for a specific customer
const orders = await client.orders.list({
  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:
// 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(),
})

Filtering by association

Use underscore notation to filter on associated model attributes:
// 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',
})

Custom search scopes

Some resources expose convenience search scopes:
ResourceScopeExample
CustomerssearchFull-text over email + first/last name
Customerswith_min_total_spentFilter by lifetime spend
Ordersmulti_searchNumber + email full-text
const customers = await client.customers.list({
  search: 'jane',
})

const vipCustomers = await client.customers.list({
  with_min_total_spent: 1000,
})

Sorting

Use the top-level sort parameter on any list endpoint. Prefix with - for descending. Follows the JSON:API sorting convention.
// 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',
})
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:
const { data: orders, meta } = await client.orders.list({
  page: 2,
  limit: 50,
})

// meta.count, meta.pages, meta.previous, meta.next ...
ParameterDefaultMaxDescription
page1Page number (1-indexed)
limit25100Records per page

Pagination metadata

Responses include a meta object:
{
  "data": [...],
  "meta": {
    "page": 2,
    "limit": 50,
    "count": 327,
    "pages": 7,
    "from": 51,
    "to": 100,
    "in": 50,
    "previous": 1,
    "next": 3
  }
}
FieldDescription
pageCurrent page number
limitRecords per page
countTotal number of matching records
pagesTotal number of pages
from / to / inPosition range / count of this page
previous / nextPrevious/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:
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'],
})

Nested expand

Use dot notation up to 4 levels deep:
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:
ResourceCommon expands
Ordersitems, fulfillments, payments, customer, discounts, adjustments, billing_address, shipping_address
Customersaddresses, store_credits, orders
Productsvariants, 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:
const orders = await client.orders.list({
  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.