Back to Spree

@spree/admin-sdk

packages/admin-sdk/README.md

5.5.011.1 KB
Original Source

@spree/admin-sdk

Official TypeScript SDK for the Spree Commerce Admin API — manage products, orders, customers, fulfillments, payments, and store configuration from server-to-server integrations or admin tooling.

Developer Preview. The Admin API is in active development and may change between minor versions. Pin to a specific version of @spree/admin-sdk in production and review the changelog before upgrading.

Installation

bash
npm install @spree/admin-sdk
# or
yarn add @spree/admin-sdk
# or
pnpm add @spree/admin-sdk

Quick start

typescript
import { createAdminClient } from '@spree/admin-sdk'

const client = createAdminClient({
  baseUrl: 'https://store.example.com',
  secretKey: 'sk_xxx',
})

// List orders
const { data: orders, meta } = await client.orders.list({
  status_eq: 'complete',
  sort: '-completed_at',
  limit: 25,
})

// Create an order in one shot
const order = await client.orders.create({
  email: '[email protected]',
  currency: 'USD',
  items: [{ variant_id: 'variant_xxx', quantity: 1 }],
  shipping_address: {
    first_name: 'Jane',
    last_name: 'Doe',
    address1: '350 Fifth Avenue',
    city: 'New York',
    postal_code: '10118',
    country_iso: 'US',
    state_abbr: 'NY',
    phone: '+1 212 555 1234',
  },
})

// Manage a customer
const customer = await client.customers.create({
  email: '[email protected]',
  first_name: 'Jane',
  last_name: 'Doe',
  tags: ['wholesale'],
})

await client.customers.addresses.create(customer.id, {
  first_name: 'Jane',
  last_name: 'Doe',
  address1: '350 Fifth Avenue',
  city: 'New York',
  postal_code: '10118',
  country_iso: 'US',
  state_abbr: 'NY',
  phone: '+1 212 555 1234',
  is_default_shipping: true,
})

Authentication

The Admin API supports two authentication methods.

Secret API key (server-to-server)

Use a secret API key (sk_…) for backend integrations. Each key carries a list of scopes granted at creation time. Never embed secret keys in client-side code, mobile apps, or public repositories.

typescript
const client = createAdminClient({
  baseUrl: 'https://store.example.com',
  secretKey: 'sk_xxx',
})

JWT bearer token (admin SPA)

Authenticate as an admin user and use the returned JWT for subsequent requests. JWT-authenticated requests use CanCanCan abilities instead of scopes.

The refresh token never appears in JSON — the server sets it as an HttpOnly cookie scoped to /api/v3/admin/auth, and the SDK sends requests with credentials: 'include' by default so the cookie flows automatically. The access token is the only credential your code holds.

typescript
// A cookie-auth SPA can start with no credentials at all and bootstrap
// from the refresh cookie:
const client = createAdminClient({ baseUrl: 'https://store.example.com' })

// Login returns { token, user } — the refresh token lands in the cookie
const { token, user } = await client.auth.login({
  email: '[email protected]',
  password: 'password123',
})
client.setToken(token)

// Refresh takes no arguments — it's driven entirely by the cookie
client.onUnauthorized(async () => {
  const { token: fresh } = await client.auth.refresh()
  client.setToken(fresh)
  return true
})

// Logout destroys the refresh token server-side and clears the cookie
await client.auth.logout()

auth.login() also accepts third-party identity-provider payloads ({ provider: 'auth0', token: '<jwt>' }) when the server has a matching strategy registered in Spree.store_authentication_strategies.

Resource clients

ClientEndpoints
client.ordersList, get, create, update, delete, complete, cancel, approve, resume, resend confirmation. Nested: items, payments (incl. capture/void), fulfillments (incl. fulfill/cancel/resume/split), refunds, giftCards, storeCredits, adjustments.
client.customersCRUD plus bulk group/tag operations. Nested: addresses, creditCards, storeCredits.
client.customerGroupsCRUD on customer groups.
client.productsCRUD, clone, and bulk operations (status, categories, channels, tags, destroy). Nested: media, variants (with their own media).
client.variantsTop-level variant search across products.
client.optionTypesCRUD on option types and values.
client.categoriesList categories.
client.tagsAutocomplete tag names per taggable type.
client.pricesCRUD plus bulkUpsert / bulkDestroy for variant prices.
client.priceListsCRUD, activate/deactivate, and price-list rule types.
client.promotionsCRUD. Nested: actions, rules, couponCodes. Companion lookups: client.promotionActions.types/calculators, client.promotionRules.types.
client.giftCards / client.giftCardBatchesGift card CRUD and batch creation.
client.storeCreditCategoriesList/read store credit categories.
client.paymentMethodsCRUD plus types (available gateway types).
client.taxCategoriesCRUD on tax categories.
client.stockLocationsCRUD on stock locations.
client.stockItemsList, get, update, delete stock items.
client.stockTransfersList, get, create, delete stock transfers.
client.channelsCRUD plus addProducts / removeProducts.
client.marketsCRUD on markets.
client.countriesList and read countries (for address dropdowns).
client.customFieldDefinitionsCRUD on custom field definitions.
client.exportsCreate and track CSV exports (products, orders, customers, …).
client.adminUsersList, get, update, delete admin users.
client.invitationsStaff invitations — list, get, create, delete, resend.
client.rolesList and read roles.
client.apiKeysCRUD plus revoke for API keys.
client.allowedOriginsCRUD on CORS allowed origins.
client.webhookEndpointsCRUD, send test, enable/disable. Nested: deliveries (list, get, redeliver).
client.dashboardSales analytics.
client.storeStore profile (get, update).
client.meCurrent admin user + permissions.
client.authLogin (email/password or identity provider), cookie-driven refresh, logout, invitation lookup/acceptance.
client.directUploadsPre-signed Active Storage uploads (used by media flows).

Custom fields

Resources that support custom fields expose a customFields accessor taking the parent ID first — client.products.customFields.list('prod_xxx') — and the generic escape hatch covers everything else:

typescript
await client.customFields('Spree::Product', 'prod_xxx').list()

Querying

Collection endpoints support Ransack filters via flat parameters:

typescript
const orders = await client.orders.list({
  status_eq: 'complete',
  total_gteq: 100,
  email_cont: '@example.com',
  user_id_eq: 'cus_xxx',           // resource IDs work directly
  sort: '-completed_at',
  page: 2,
  limit: 50,
  expand: ['items', 'customer'],
})

The SDK wraps filter keys in q[…] automatically.

Error handling

Every non-2xx response throws a SpreeError:

typescript
import { SpreeError } from '@spree/admin-sdk'

try {
  await client.orders.update(orderId, { email })
} catch (err) {
  if (err instanceof SpreeError) {
    console.log(err.code)    // e.g. 'cart_already_updated'
    console.log(err.status)  // e.g. 409
    console.log(err.details) // optional structured context
  }
}

When a request fails because the API key lacks the required scope, the error has code: 'access_denied' and details.required_scope carries the missing scope name.

TypeScript support

Full TypeScript support with generated types from the API serializers:

typescript
import type {
  AdminOrder,
  AdminProduct,
  AdminCustomer,
  AdminFulfillment,
  AdminPayment,
  PaginatedResponse,
} from '@spree/admin-sdk'

const orders: PaginatedResponse<AdminOrder> = await client.orders.list()
const product: AdminProduct = await client.products.get('prod_xxx')

Admin types are exported with the Admin prefix to distinguish them from the customer-facing @spree/sdk types (which use Store prefixes for the same domain entities). Admin types include fields and relationships hidden from the Store API.

Custom fetch

You can provide a custom fetch implementation:

typescript
const client = createAdminClient({
  baseUrl: 'https://store.example.com',
  secretKey: 'sk_xxx',
  fetch: customFetchImplementation,
})

Documentation

Development

Setup

bash
cd packages/admin-sdk
pnpm install

Scripts

CommandDescription
pnpm testRun tests once
pnpm test:watchRun tests in watch mode
pnpm typecheckType-check with tsc --noEmit
pnpm lintLint with Biome
pnpm lint:fixLint and auto-fix with Biome
pnpm formatFormat source with Biome
pnpm buildBuild CJS + ESM bundles with tsup
pnpm devBuild in watch mode
pnpm generate:admin-clientRegenerate the resource client from the OpenAPI spec

Type generation pipeline

When the upstream Admin API serializers in spree/api change, regenerate types from the monorepo root:

bash
# 1. Regenerate TypeScript types from Alba serializers
cd spree/api && bundle exec rake typelizer:generate

# 2. Rebuild the SDK (consumes the generated types)
cd packages/admin-sdk && pnpm build

# 3. Run tests to confirm nothing broke
pnpm test

Generated TypeScript types land in src/types/generated/; do not edit by hand.

Releasing

This package uses Changesets for version management and publishing.

After making changes:

bash
pnpm changeset

This prompts you to select a semver bump type (patch/minor/major) and write a summary. A changeset file is created in .changeset/.

How releases work:

  1. Changeset files are committed with your PR
  2. When merged to main, a GitHub Action creates a "Version Packages" PR that bumps the version and updates the CHANGELOG
  3. When that PR is merged, the package is automatically published to npm under the next dist-tag (Developer Preview), so npm install @spree/admin-sdk does not pick it up as latest

Manual release (if needed):

bash
pnpm version   # Apply changesets and bump version
pnpm release   # Build and publish to npm

License

MIT