Back to Spree

Admin API

docs/plans/6.0-admin-api.md

5.5.039.5 KB
Original Source

Admin API

Status: In Progress Target: Spree 6.0 Depends on: 6.0-admin-spa.md, 6.0-platform-auth.md Author: damian Last updated: 2026-04-11

Summary

A new REST API namespace (/api/v3/admin/*) that exposes every admin resource and operation currently served by the spree/admin Rails engine. Provides the programmatic backbone for the new React admin SPA, the TypeScript SDK, the CLI, and any other admin-facing tooling. Follows the same conventions as the Store API (flat JSON, prefixed IDs, Ransack filters, Pagy pagination, CanCanCan authorization) with a separate JWT audience and admin-scoped authentication.

Key Decisions (do not deviate without discussion)

  • All controllers inherit from Spree::Api::V3::Admin::ResourceController — no bespoke controllers unless absolutely necessary.
  • RESTful onlyPOST to create, PATCH to update, DELETE to destroy. No custom verbs except for state-machine actions (/cancel, /approve, etc.).
  • Admin serializers extend Store API serializers — inherit the public fields and add admin-only ones. Never duplicate attribute definitions.
  • Flat JSON for nested resources — no Rails nested attributes. Target audience is TypeScript developers, not Rails devs.
  • Prefixed IDs everywhere — requests and responses. Including association IDs.
  • Separate JWT audience (admin_api) — admin tokens MUST NOT be valid on the Store API and vice versa. Uses Spree.admin_user_class.
  • Two auth mechanisms, both already infrastructured:
    1. Secret API keys (spree_sk_xxx) via Authorization: Bearer for server-to-server
    2. JWT tokens via Authorization: Bearer for SPA sessions
  • Stripe-style error responses — same format as Store API (see docs/api-reference/store-api/errors.mdx).
  • Use existing services for business logic — cart create, order update, line item CRUD, etc. Only create new services when direct record.update! would embed non-trivial business rules in the controller.
  • Max 2 levels of nesting — e.g., /orders/:id/line_items/:id is fine, /orders/:id/shipments/:id/rates/:id is not. Flatten with filter params when necessary.
  • Attachments via multipart/form-data — ActiveStorage underneath. Product images use Spree::Asset (ActiveStorage + alt text + ordering + variant scoping).
  • Typelizer generates TypeScript types after every serializer change — part of the PR checklist; never merge a serializer change without regenerating.

Design Details

Controller hierarchy

Spree::Api::V3::BaseController           # shared concerns: pagination, Ransack, caching
  └── Spree::Api::V3::Admin::BaseController
        └── Spree::Api::V3::Admin::ResourceController  # default CRUD
              └── Spree::Api::V3::Admin::ProductsController
              └── Spree::Api::V3::Admin::OrdersController
              └── ...

ResourceController provides a default index/show/create/update/destroy implementation. Subclasses override model_class, serializer_class, and optionally scope, permitted_params, find_resource, scope_includes. Most admin controllers should fit in 30–80 lines.

Authentication

  • Spree::Api::V3::AdminAuthentication concern added to Admin::BaseController
  • Tries secret API key first, falls back to JWT with admin audience
  • Refresh tokens via Spree::RefreshToken — rotated on every refresh
  • /auth/login, /auth/refresh live on Admin::AuthController which skips authenticate_admin!
  • Returns { token, refresh_token, user } shape

Response format

  • Flat JSON, no root key wrapping ({ id: ..., name: ... } not { product: { id: ... } })
  • Collections return { data: [...], meta: { total_count, page, pages, limit } }
  • Prefixed IDs everywhere (prod_86Rf07xd4z, variant_k5nR8xLq)
  • ?expand=variants,variants.prices to include associations (max 4 levels)
  • ?q[name_cont]=shirt for Ransack filtering
  • ?sort=-created_at for sorting (JSON:API convention)

Example: product create/update

json
{
  "name": "Test product",
  "tax_category_id": "taxcat_abc1234",
  "taxon_ids": ["taxon_abc123", "taxon_def456"],
  "tags": ["eco", "best-seller"],
  "variants": [
    {
      "option_type": "size",
      "option_value": "small",
      "total_on_hand": 10,
      "track_inventory": true,
      "width": 10.5,
      "height": 5.2,
      "depth": 2.1,
      "weight": 0.5,
      "dimensions_unit": "cm",
      "weight_unit": "kg",
      "prices": [
        { "currency": "USD", "amount": 10.99 },
        { "currency": "EUR", "amount": 9.99 }
      ]
    }
  ]
}

Validation errors return 422 with:

json
{
  "error": {
    "code": "validation_error",
    "message": "Name can't be blank",
    "details": { "name": ["can't be blank"] }
  }
}

Example: order create with line items (one call)

json
{
  "currency": "USD",
  "locale": "en-US",
  "line_items": [
    { "variant_id": "variant_123456789", "quantity": 1 },
    { "variant_id": "variant_987654321", "quantity": 2 }
  ]
}

Permissions / GET /me

GET /api/v3/admin/me returns the current admin user plus a serialized list of their permissions (CanCanCan rules flattened). The SPA consumes this to drive UI permission checks; the server still enforces authorize! on every action.

json
{
  "user": { "id": "admin_xxx", "email": "...", ... },
  "permissions": [
    { "allow": true, "actions": ["manage"], "subjects": ["all"], "has_conditions": false },
    { "allow": false, "actions": ["destroy"], "subjects": ["Spree::Order"], "has_conditions": true }
  ]
}

Per-record conditions (CanCanCan blocks/hashes) are not serialized — only has_conditions: true as a hint. The SPA shows actions optimistically and handles 403 from the API.

Testing

  • Controller specs first; save integration/rswag specs for after the design solidifies per endpoint.
  • Serializer specs for attribute coverage — keep controller specs focused on behavior, not field-by-field assertions.
  • Lean and pragmatic. Cover known edge cases, skip trivial validations.

Migration Path

Infrastructure (done)

  • Base controllers, resource controller, admin authentication, JWT with admin audience, secret API key auth, prefixed IDs, error handling, Pagy, Ransack, CanCanCan scoping — all in place.
  • Initial admin serializers exist for Product, Variant, Order, Customer, Price, Metafield, Taxon, Taxonomy, LineItem. More are added as endpoints come online.

Endpoint rollout order

  1. Authentication — login, refresh, me ✅
  2. Products + Variants + Assets + Options — highest-priority, hardest, nested
  3. Orders + Line Items + Shipments + Payments + Adjustments + Refunds
  4. Customers + Addresses + Store Credits + Gift Cards
  5. Promotions + Rules + Actions
  6. Stock + Stock Transfers + Price Lists
  7. Taxonomy + Classifications
  8. Store settings, Policies, Admin users, Roles, Invitations
  9. Metafields / Custom Fields + Translations (polymorphic)
  10. Webhooks, API keys, Integrations
  11. Reports, Exports, Imports, Dashboard analytics

PR process for each endpoint

  1. Add controller and route
  2. Add/extend admin serializer
  3. Run bundle exec rake typelizer:generate + pnpm generate:zod
  4. Add controller spec (+ serializer spec if new attributes)
  5. Add SDK resource in @spree/admin-sdk
  6. Mark endpoint [x] in this plan
  7. If public-facing: update OpenAPI spec with bundle exec rake rswag:specs:swaggerize

Constraints on Current Work

  • Never add endpoints outside Admin::ResourceController hierarchy without a written exception in this plan.
  • Never expose raw IDs — always prefixed.
  • Never use Rails nested attributes in the API layer.
  • Never duplicate a Store serializer — extend it instead.
  • Never merge a serializer change without running Typelizer.
  • Never create new services unless absolutely necessary; record.update is almost always enough.

Changes from 5.x to 6.0

This plan reflects the 6.0 model/naming landscape. Endpoints removed from 5.x or renamed are tracked in the Removed / Renamed endpoints table at the top so contributors don't accidentally re-implement them.

Removed / renamed endpoints

5.x endpoint6.0 endpointReason
/api/v3/admin/taxonomies/*removedSpree::Taxonomy dropped; Category belongs directly to store. See 6.0-replace-taxons-with-categories.md
/api/v3/admin/taxons/*/api/v3/admin/categories/*Spree::TaxonSpree::Category rename
/api/v3/admin/shipping_methods/*/api/v3/admin/delivery_methods/*ShippingMethodDeliveryMethod rename
/api/v3/admin/orders/:id/shipments/*/api/v3/admin/orders/:id/fulfillments/*ShipmentFulfillment rename
/api/v3/admin/shipping_categories/*removedReplaced by ProductType.fulfillment_types. See 6.0-fulfillment-and-delivery.md
/api/v3/admin/zones/*removedTax zones → direct country/state FKs on TaxRate. Shipping zones → new DeliveryZone. See 6.0-tax-provider.md, 6.0-fulfillment-and-delivery.md
/api/v3/admin/return_authorizations/*/api/v3/admin/returns/*ReturnAuthorizationReturn. See 6.0-returns-exchanges-claims.md
/api/v3/admin/customer_returns/*removedMerged into Return + ReturnItem
/api/v3/admin/orders/:id/reimbursements/*removedReplaced by direct Refund on Returns/Exchanges/Claims
/api/v3/admin/reimbursement_types/*removedConcept dropped
/api/v3/admin/prototypes/*/api/v3/admin/product_types/*PrototypeProductType rename. See 6.0-product-types.md
/api/v3/admin/metafield_definitions/*/api/v3/admin/custom_field_definitions/*MetafieldCustomField rename. See 5.4-6.0-custom-fields-rename.md
/api/v3/admin/orders/:id/adjustments/*/api/v3/admin/orders/:id/tax_lines, discounts, fees (read-only)Polymorphic Adjustment → typed TaxLine, Discount, Fee. See 6.0-split-adjustments.md
?expand=master_variant?expand=default_variantis_master dropped, default_variant_id FK. See 6.0-remove-master-variant.md
?expand=images?expand=mediaImage / AssetMedia. See 5.4-6.0-product-media-system.md

Field renames on request / response bodies

The following field names change across all endpoints. See 5.4-store-api-naming-standardization.md for the full list.

Model5.x6.0
Paymentstatestatus
Fulfillment (was Shipment)statestatus
Fulfillmentshipped_atfulfilled_at
FulfillmentItem (was InventoryUnit)statestatus
Return (was ReturnAuthorization)statestatus
GiftCardstatestatus
Ordershipment_statefulfillment_status
Orderpayment_statepayment_status
Ordershipment_totaldelivery_total
Orderpromo_totaldiscount_total
Orderitem_counttotal_quantity
Orderspecial_instructionscustomer_note
Orderbill_address_idbilling_address_id
Ordership_address_idshipping_address_id
LineItempromo_totaldiscount_total
Addressfirstnamefirst_name
Addresslastnamelast_name
Addresszipcodepostal_code
OptionType, OptionValue, CustomFieldDefinitionpresentationlabel
CreditCardcc_typebrand
CreditCardlast_digitslast4
CustomFieldDefinitiondisplay_on (string)storefront_visible (boolean)
CustomFieldDefinitionmetafield_typefield_type

Prefix ID changes

Also tracked in the SDK type definitions. See each linked plan for rationale.

Model5.x prefix6.0 prefix
Fulfillment (was Shipment)shipment_ful_
DeliveryMethod (was ShippingMethod)dm_
DeliveryRate (was ShippingRate)dr_
FulfillmentItem (was InventoryUnit)fi_
DeliveryZone (new)dz_
StockReservation (new)res_
StockMovementsm_
Return (was ReturnAuthorization)ret_
Exchange (new)exch_
Claim (new)claim_
Channel (new)ch_
Catalog (new)cat_
Company (new)comp_
CompanyLocation (new)cloc_
CompanyContact (new)cc_
ProductPublication (replaces StoreProduct)pp_
Collection (new)coll_
ProductType (was Prototype)proto_pt_
Customer (was User)user_cust_
AdminUseradmin_adm_
CustomField (was Metafield)mf_cf_
CustomFieldDefinition (was MetafieldDefinition)mfdef_cfdef_
Media (was Image/Asset)image_/asset_media_

Endpoint list

Each endpoint below is tracked as [ ] (pending) or [x] (shipped). When you implement one, flip the checkbox in the same PR. Endpoints are grouped by resource. Removed/renamed 5.x endpoints are NOT listed — see the table above.

Authentication

  • POST /api/v3/admin/auth/login — admin user login (JWT + refresh token)
  • POST /api/v3/admin/auth/refresh — rotate refresh token, return new JWT
  • DELETE /api/v3/admin/auth/logout — invalidate JWT and refresh token
  • GET /api/v3/admin/me — current admin user + serialized permissions

Products (⭐ high priority)

  • GET /api/v3/admin/products — list (Ransack, sort, paginate)
  • GET /api/v3/admin/products/:id
  • POST /api/v3/admin/products — with nested variants, prices, category_ids
  • PATCH /api/v3/admin/products/:id
  • DELETE /api/v3/admin/products/:id — soft delete
  • POST /api/v3/admin/products/:id/clone
  • PATCH /api/v3/admin/products/bulk_update — bulk status/category/tags

6.0 field changes: shipping_category_id is dropped (fulfillment types come from ProductType.fulfillment_types). prototype_idproduct_type_id. taxon_idscategory_ids. Product has default_variant_id FK (no more is_master).

Variants (nested under Products)

  • GET /api/v3/admin/products/:product_id/variants
  • GET /api/v3/admin/products/:product_id/variants/:id
  • POST /api/v3/admin/products/:product_id/variants — with nested prices
  • PATCH /api/v3/admin/products/:product_id/variants/:id
  • DELETE /api/v3/admin/products/:product_id/variants/:id

Prices

Managed via nested variant params — no standalone endpoints.

Product Media (nested under Products, replaces Assets)

  • GET /api/v3/admin/products/:product_id/media — list (filterable by media_type)
  • POST /api/v3/admin/products/:product_id/media — upload via multipart OR source_url param (async download). Params: media_type, alt, position, variant_ids
  • PATCH /api/v3/admin/products/:product_id/media/:id — update alt, position, variant assignment
  • DELETE /api/v3/admin/products/:product_id/media/:id

Digital Assets (nested under Products)

  • GET /api/v3/admin/products/:product_id/digital_assets
  • POST /api/v3/admin/products/:product_id/digital_assets
  • PATCH /api/v3/admin/products/:product_id/digital_assets/:id
  • DELETE /api/v3/admin/products/:product_id/digital_assets/:id

Option Types

  • GET /api/v3/admin/option_types
  • GET /api/v3/admin/option_types/:id
  • POST /api/v3/admin/option_types
  • PATCH /api/v3/admin/option_types/:id
  • DELETE /api/v3/admin/option_types/:id

Option Values (nested under Option Types)

  • GET /api/v3/admin/option_types/:option_type_id/option_values
  • POST /api/v3/admin/option_types/:option_type_id/option_values
  • PATCH /api/v3/admin/option_types/:option_type_id/option_values/:id
  • DELETE /api/v3/admin/option_types/:option_type_id/option_values/:id

Product Types (renamed from Prototypes)

  • GET /api/v3/admin/product_types
  • GET /api/v3/admin/product_types/:id
  • POST /api/v3/admin/product_types
  • PATCH /api/v3/admin/product_types/:id
  • DELETE /api/v3/admin/product_types/:id

6.0: fulfillment_types JSON array column (['shipping'] default). Product types can require specific CustomFieldDefinitions via nested definition records. See 6.0-product-types.md.

Categories (renamed from Taxons; Taxonomy dropped)

  • GET /api/v3/admin/categories — list with tree metadata
  • GET /api/v3/admin/categories/:id
  • POST /api/v3/admin/categories
  • PATCH /api/v3/admin/categories/:id
  • DELETE /api/v3/admin/categories/:id
  • PATCH /api/v3/admin/categories/:id/reposition — reposition in tree

6.0: Spree::Category belongs directly to Spree::Store (no Taxonomy). ClassificationProductCategory. See 6.0-replace-taxons-with-categories.md.

Product Categories (nested under Categories — product assignments)

  • GET /api/v3/admin/categories/:category_id/products
  • POST /api/v3/admin/categories/:category_id/products — assign
  • PATCH /api/v3/admin/categories/:category_id/products/:product_id — update position
  • DELETE /api/v3/admin/categories/:category_id/products/:product_id — unassign

Collections (new in 6.0)

  • GET /api/v3/admin/collections
  • GET /api/v3/admin/collections/:id
  • POST /api/v3/admin/collections — with nested rules (automatic) or manual product list
  • PATCH /api/v3/admin/collections/:id
  • DELETE /api/v3/admin/collections/:id

6.0: Collections replace the "smart taxon" (automatic: true, rules_match_policy) feature. CollectionRule replaces TaxonRule. See 6.0-replace-taxons-with-categories.md.

Channels (new in 6.0)

  • GET /api/v3/admin/channels
  • GET /api/v3/admin/channels/:id
  • POST /api/v3/admin/channels
  • PATCH /api/v3/admin/channels/:id
  • DELETE /api/v3/admin/channels/:id

6.0: A Channel is a sales surface (POS, Storefront A, Marketplace, etc.). See 6.0-channels-catalogs-b2b.md.

Product Listings (nested under Channels, replaces StoreProduct)

  • GET /api/v3/admin/channels/:channel_id/product_publications
  • POST /api/v3/admin/channels/:channel_id/product_publications
  • DELETE /api/v3/admin/channels/:channel_id/product_publications/:id
  • POST /api/v3/admin/channels/:channel_id/product_publications/bulk — bulk add/remove

Catalogs (new in 6.0, B2B)

  • GET /api/v3/admin/catalogs
  • GET /api/v3/admin/catalogs/:id
  • POST /api/v3/admin/catalogs
  • PATCH /api/v3/admin/catalogs/:id
  • DELETE /api/v3/admin/catalogs/:id
  • GET /api/v3/admin/catalogs/:id/products
  • POST /api/v3/admin/catalogs/:id/products — bulk assign
  • DELETE /api/v3/admin/catalogs/:id/products/:product_id
  • POST /api/v3/admin/catalogs/:id/assign — assign catalog to companies/locations

Companies (new in 6.0, B2B)

  • GET /api/v3/admin/companies
  • GET /api/v3/admin/companies/:id
  • POST /api/v3/admin/companies
  • PATCH /api/v3/admin/companies/:id
  • DELETE /api/v3/admin/companies/:id

Company Locations (nested under Companies)

  • GET /api/v3/admin/companies/:company_id/locations
  • POST /api/v3/admin/companies/:company_id/locations
  • GET /api/v3/admin/company_locations/:id
  • PATCH /api/v3/admin/company_locations/:id
  • DELETE /api/v3/admin/company_locations/:id

Company Contacts (nested under Company Locations)

  • GET /api/v3/admin/company_locations/:location_id/contacts
  • POST /api/v3/admin/company_locations/:location_id/contacts
  • PATCH /api/v3/admin/company_contacts/:id
  • DELETE /api/v3/admin/company_contacts/:id

Orders (⭐ high priority)

  • GET /api/v3/admin/orders
  • GET /api/v3/admin/orders/:id
  • POST /api/v3/admin/orders — create draft
  • PATCH /api/v3/admin/orders/:id
  • DELETE /api/v3/admin/orders/:id
  • PATCH /api/v3/admin/orders/:id/complete
  • PATCH /api/v3/admin/orders/:id/cancel
  • PATCH /api/v3/admin/orders/:id/approve
  • PATCH /api/v3/admin/orders/:id/resume
  • POST /api/v3/admin/orders/:id/resend_confirmation

6.0: Order state machine dropped (derived status from line items + fulfillments + payments — see 6.0-cart-order-split.md). PATCH /next and /advance no longer apply and have been removed from the 5.x admin API ahead of the 6.0 cart/order split. Cart and Order are separate models; LineItem.owner is polymorphic. Order gains channel_id, company_location_id, withdrawal_eligible_until FKs.

Items (nested under Orders — matches Store API /carts/:id/items convention)

  • GET /api/v3/admin/orders/:order_id/items
  • GET /api/v3/admin/orders/:order_id/items/:id
  • POST /api/v3/admin/orders/:order_id/items
  • PATCH /api/v3/admin/orders/:order_id/items/:id
  • DELETE /api/v3/admin/orders/:order_id/items/:id

Fulfillments (nested under Orders; replaces Shipments)

  • GET /api/v3/admin/orders/:order_id/fulfillments
  • GET /api/v3/admin/orders/:order_id/fulfillments/:id
  • POST /api/v3/admin/orders/:order_id/fulfillments
  • PATCH /api/v3/admin/orders/:order_id/fulfillments/:id
  • PATCH /api/v3/admin/orders/:order_id/fulfillments/:id/fulfill — mark as fulfilled
  • PATCH /api/v3/admin/orders/:order_id/fulfillments/:id/cancel
  • PATCH /api/v3/admin/orders/:order_id/fulfillments/:id/resume
  • PATCH /api/v3/admin/orders/:order_id/fulfillments/:id/split

6.0: Third-party fulfillment via FulfillmentProvider interface. pickup_point_data JSON snapshot for pickup points. See 6.0-fulfillment-and-delivery.md.

Payments (nested under Orders)

  • GET /api/v3/admin/orders/:order_id/payments
  • GET /api/v3/admin/orders/:order_id/payments/:id
  • POST /api/v3/admin/orders/:order_id/payments — supports off-session admin charges via source_id (saved Spree::CreditCard belonging to the order's customer)
  • PATCH /api/v3/admin/orders/:order_id/payments/:id/capture
  • PATCH /api/v3/admin/orders/:order_id/payments/:id/void

Off-session charges: pass { payment_method_id, source_id, amount } to charge a saved card. There is no separate "PaymentSession" endpoint on the admin side — the cart/store API has payment_sessions for SCA/3DS storefront flows; admin-initiated charges use saved sources directly.

Refunds (nested under Orders)

  • GET /api/v3/admin/orders/:order_id/refunds
  • POST /api/v3/admin/orders/:order_id/refunds — with payment_id in body
  • PATCH /api/v3/admin/orders/:order_id/refunds/:id

Adjustments (nested under Orders, read-only in 5.x; replaced by typed endpoints in 6.0)

  • GET /api/v3/admin/orders/:order_id/adjustments
  • GET /api/v3/admin/orders/:order_id/adjustments/:id

5.x → 6.0: The 5.x admin API exposes /adjustments as read-only (no create/update/destroy) so client integrations don't depend on a write surface that is going away. Manual adjustment management has been removed. In 6.0, this endpoint is replaced by the three typed endpoints below.

Tax Lines (nested under Orders, read-only; replaces Adjustments in 6.0)

  • GET /api/v3/admin/orders/:order_id/tax_lines

Discounts (nested under Orders, read-only; replaces Adjustments in 6.0)

  • GET /api/v3/admin/orders/:order_id/discounts

Fees (nested under Orders, read-only; replaces Adjustments in 6.0)

  • GET /api/v3/admin/orders/:order_id/fees

6.0: Polymorphic Spree::Adjustment is dropped. Tax, discount, and fee data is returned via typed, read-only endpoints. See 6.0-split-adjustments.md.

Order Customer Assignment

Customer assignment is handled via the standard PATCH /api/v3/admin/orders/:id endpoint with customer_id in the body. No dedicated endpoint. Pass customer_id: null to unassign.

  • PATCH /api/v3/admin/orders/:id { customer_id } — assign / reassign / unassign customer (5.x bridge: also accepts user_id)

Order Gift Cards (nested under Orders)

  • POST /api/v3/admin/orders/:order_id/gift_cards { code } — apply a gift card by code
  • DELETE /api/v3/admin/orders/:order_id/gift_cards/:id — remove the applied gift card

Order Store Credits (nested under Orders)

  • POST /api/v3/admin/orders/:order_id/store_credits { amount? } — apply customer's available store credit (auto up to balance, or a specific amount)
  • DELETE /api/v3/admin/orders/:order_id/store_credits — remove store credit application

Order Discounts (promotion application)

  • POST /api/v3/admin/orders/:order_id/discounts — apply promotion or coupon
  • DELETE /api/v3/admin/orders/:order_id/discounts/:id — remove

Returns (new in 6.0)

  • GET /api/v3/admin/returns
  • GET /api/v3/admin/orders/:order_id/returns
  • GET /api/v3/admin/returns/:id
  • POST /api/v3/admin/orders/:order_id/returns
  • PATCH /api/v3/admin/returns/:id
  • DELETE /api/v3/admin/returns/:id
  • POST /api/v3/admin/returns/:id/approve
  • POST /api/v3/admin/returns/:id/receive
  • POST /api/v3/admin/returns/:id/refund

Return Items (nested under Returns)

  • PATCH /api/v3/admin/returns/:return_id/items/:id — reception + acceptance status

Exchanges (new in 6.0)

  • GET /api/v3/admin/exchanges
  • GET /api/v3/admin/orders/:order_id/exchanges
  • GET /api/v3/admin/exchanges/:id
  • POST /api/v3/admin/orders/:order_id/exchanges
  • PATCH /api/v3/admin/exchanges/:id
  • POST /api/v3/admin/exchanges/:id/approve
  • POST /api/v3/admin/exchanges/:id/receive
  • POST /api/v3/admin/exchanges/:id/fulfill

Claims (new in 6.0)

  • GET /api/v3/admin/claims
  • GET /api/v3/admin/orders/:order_id/claims
  • GET /api/v3/admin/claims/:id
  • POST /api/v3/admin/orders/:order_id/claims
  • PATCH /api/v3/admin/claims/:id
  • POST /api/v3/admin/claims/:id/approve
  • POST /api/v3/admin/claims/:id/resolve

6.0: Returns, Exchanges, and Claims are first-class models. Replaces the ReturnAuthorization → CustomerReturn → Reimbursement chain. See 6.0-returns-exchanges-claims.md.

Customers (users)

  • GET /api/v3/admin/customers
  • GET /api/v3/admin/customers/:id
  • POST /api/v3/admin/customers
  • PATCH /api/v3/admin/customers/:id
  • DELETE /api/v3/admin/customers/:id
  • POST /api/v3/admin/customers/:id/anonymize — GDPR right to erasure
  • GET /api/v3/admin/customers/:id/export — GDPR data export (returns JSON download URL)

6.0: Spree.user_classSpree.customer_class. Spree::LegacyUserSpree::Customer. Anonymize + export endpoints from 5.4-6.0-eu-legal-compliance.md.

Customer Addresses (nested under Customers)

  • GET /api/v3/admin/customers/:customer_id/addresses
  • POST /api/v3/admin/customers/:customer_id/addresses
  • PATCH /api/v3/admin/customers/:customer_id/addresses/:id
  • DELETE /api/v3/admin/customers/:customer_id/addresses/:id

Store Credits (nested under Customers)

  • GET /api/v3/admin/customers/:customer_id/store_credits
  • GET /api/v3/admin/customers/:customer_id/store_credits/:id
  • POST /api/v3/admin/customers/:customer_id/store_credits
  • PATCH /api/v3/admin/customers/:customer_id/store_credits/:id
  • DELETE /api/v3/admin/customers/:customer_id/store_credits/:id

Gift Cards

  • GET /api/v3/admin/gift_cards
  • GET /api/v3/admin/gift_cards/:id
  • POST /api/v3/admin/gift_cards
  • PATCH /api/v3/admin/gift_cards/:id
  • DELETE /api/v3/admin/gift_cards/:id
  • POST /api/v3/admin/gift_card_batches — batch create

Customer Groups

  • GET /api/v3/admin/customer_groups
  • GET /api/v3/admin/customer_groups/:id
  • POST /api/v3/admin/customer_groups
  • PATCH /api/v3/admin/customer_groups/:id
  • DELETE /api/v3/admin/customer_groups/:id

Customer Group Members (nested under Customer Groups)

  • GET /api/v3/admin/customer_groups/:customer_group_id/members
  • POST /api/v3/admin/customer_groups/:customer_group_id/members — bulk add
  • DELETE /api/v3/admin/customer_groups/:customer_group_id/members/:id
  • DELETE /api/v3/admin/customer_groups/:customer_group_id/members — bulk remove

Newsletter Subscribers

  • GET /api/v3/admin/newsletter_subscribers
  • DELETE /api/v3/admin/newsletter_subscribers/:id

Tax Exemption Certificates (nested under Companies; B2B)

  • GET /api/v3/admin/companies/:company_id/tax_exemption_certificates
  • POST /api/v3/admin/companies/:company_id/tax_exemption_certificates
  • PATCH /api/v3/admin/tax_exemption_certificates/:id
  • DELETE /api/v3/admin/tax_exemption_certificates/:id

Promotions

  • GET /api/v3/admin/promotions
  • GET /api/v3/admin/promotions/:id
  • POST /api/v3/admin/promotions
  • PATCH /api/v3/admin/promotions/:id
  • DELETE /api/v3/admin/promotions/:id
  • POST /api/v3/admin/promotions/:id/clone

Promotion Actions (nested under Promotions)

  • GET /api/v3/admin/promotions/:promotion_id/actions
  • POST /api/v3/admin/promotions/:promotion_id/actions
  • PATCH /api/v3/admin/promotions/:promotion_id/actions/:id
  • DELETE /api/v3/admin/promotions/:promotion_id/actions/:id

Promotion Rules (nested under Promotions)

  • GET /api/v3/admin/promotions/:promotion_id/rules
  • POST /api/v3/admin/promotions/:promotion_id/rules
  • PATCH /api/v3/admin/promotions/:promotion_id/rules/:id
  • DELETE /api/v3/admin/promotions/:promotion_id/rules/:id

Coupon Codes (nested under Promotions, read-only)

  • GET /api/v3/admin/promotions/:promotion_id/coupon_codes

Stock Locations

  • GET /api/v3/admin/stock_locations
  • GET /api/v3/admin/stock_locations/:id
  • POST /api/v3/admin/stock_locations
  • PATCH /api/v3/admin/stock_locations/:id
  • DELETE /api/v3/admin/stock_locations/:id

Stock Items

  • GET /api/v3/admin/stock_items — filterable by stock_location, variant
  • PATCH /api/v3/admin/stock_items/:id
  • DELETE /api/v3/admin/stock_items/:id

Stock Movements (read-only; typed in 6.0)

  • GET /api/v3/admin/stock_movements — filterable by kind, fulfillment_id, order_id, return_id, stock_transfer_id

6.0: StockMovement gains kind enum (received, allocated, shipped, released, adjusted) + concrete FKs to the originating event. See 6.0-typed-stock-movements.md.

Stock Reservations (read-only; new in 6.0)

  • GET /api/v3/admin/stock_reservations — filterable by variant, order, cart
  • DELETE /api/v3/admin/stock_reservations/:id — manual release

6.0: Time-limited reservations during checkout. See 6.0-stock-reservations.md.

Stock Transfers

  • GET /api/v3/admin/stock_transfers
  • GET /api/v3/admin/stock_transfers/:id
  • POST /api/v3/admin/stock_transfers
  • DELETE /api/v3/admin/stock_transfers/:id

Price Lists

  • GET /api/v3/admin/price_lists
  • GET /api/v3/admin/price_lists/:id
  • POST /api/v3/admin/price_lists
  • PATCH /api/v3/admin/price_lists/:id
  • DELETE /api/v3/admin/price_lists/:id

Price Rules (nested under Price Lists)

  • GET /api/v3/admin/price_lists/:price_list_id/price_rules
  • POST /api/v3/admin/price_lists/:price_list_id/price_rules
  • PATCH /api/v3/admin/price_lists/:price_list_id/price_rules/:id
  • DELETE /api/v3/admin/price_lists/:price_list_id/price_rules/:id

Price List Products (nested under Price Lists)

  • GET /api/v3/admin/price_lists/:price_list_id/products
  • POST /api/v3/admin/price_lists/:price_list_id/products — bulk add
  • DELETE /api/v3/admin/price_lists/:price_list_id/products — bulk remove

Price History (read-only; EU Omnibus compliance)

  • GET /api/v3/admin/products/:product_id/price_history — per-variant history for Omnibus "lowest price in 30 days"

6.0: See 5.4-6.0-eu-legal-compliance.md.

Payment Methods

  • GET /api/v3/admin/payment_methods
  • GET /api/v3/admin/payment_methods/:id
  • POST /api/v3/admin/payment_methods
  • PATCH /api/v3/admin/payment_methods/:id
  • DELETE /api/v3/admin/payment_methods/:id

Delivery Methods (replaces Shipping Methods)

  • GET /api/v3/admin/delivery_methods
  • GET /api/v3/admin/delivery_methods/:id
  • POST /api/v3/admin/delivery_methods
  • PATCH /api/v3/admin/delivery_methods/:id
  • DELETE /api/v3/admin/delivery_methods/:id

6.0: No more calculator_id. New fields: rate_provider (class name), fulfillment_provider (class name), pickup_point_provider (for third-party pickup), fulfillment_type (enum: shipping, pickup, pickup_point, digital, local_delivery). See 6.0-delivery-rate-provider.md, 6.0-fulfillment-and-delivery.md.

Delivery Zones (new in 6.0, replaces shipping Zones)

  • GET /api/v3/admin/delivery_zones
  • GET /api/v3/admin/delivery_zones/:id
  • POST /api/v3/admin/delivery_zones
  • PATCH /api/v3/admin/delivery_zones/:id
  • DELETE /api/v3/admin/delivery_zones/:id

Delivery Zone Members (nested)

  • GET /api/v3/admin/delivery_zones/:zone_id/members
  • POST /api/v3/admin/delivery_zones/:zone_id/members
  • DELETE /api/v3/admin/delivery_zones/:zone_id/members/:id

Tax Categories

  • GET /api/v3/admin/tax_categories
  • GET /api/v3/admin/tax_categories/:id
  • POST /api/v3/admin/tax_categories
  • PATCH /api/v3/admin/tax_categories/:id
  • DELETE /api/v3/admin/tax_categories/:id

Tax Rates

  • GET /api/v3/admin/tax_rates
  • GET /api/v3/admin/tax_rates/:id
  • POST /api/v3/admin/tax_rates
  • PATCH /api/v3/admin/tax_rates/:id
  • DELETE /api/v3/admin/tax_rates/:id

6.0: zone_id FK is dropped. TaxRate gets direct country_id + state_id FKs. Tax logic moves behind a TaxProvider interface. See 6.0-tax-provider.md.

Markets

  • GET /api/v3/admin/markets
  • GET /api/v3/admin/markets/:id
  • POST /api/v3/admin/markets
  • PATCH /api/v3/admin/markets/:id
  • DELETE /api/v3/admin/markets/:id

Countries

  • GET /api/v3/admin/countries
  • GET /api/v3/admin/countries/:id

Store Settings

  • GET /api/v3/admin/store
  • PATCH /api/v3/admin/store

Policies

  • GET /api/v3/admin/policies
  • GET /api/v3/admin/policies/:id
  • POST /api/v3/admin/policies
  • PATCH /api/v3/admin/policies/:id
  • DELETE /api/v3/admin/policies/:id

Admin Users

  • GET /api/v3/admin/admin_users
  • GET /api/v3/admin/admin_users/:id
  • POST /api/v3/admin/admin_users
  • PATCH /api/v3/admin/admin_users/:id
  • DELETE /api/v3/admin/admin_users/:id

6.0: Spree::LegacyAdminUserSpree::AdminUser. Owns auth stack (no Devise). See 6.0-platform-auth.md.

Roles

  • GET /api/v3/admin/roles
  • GET /api/v3/admin/roles/:id
  • POST /api/v3/admin/roles
  • PATCH /api/v3/admin/roles/:id
  • DELETE /api/v3/admin/roles/:id

Invitations

  • GET /api/v3/admin/invitations
  • POST /api/v3/admin/invitations
  • DELETE /api/v3/admin/invitations/:id
  • PATCH /api/v3/admin/invitations/:id/resend

Custom Field Definitions (renamed from Metafield Definitions)

  • GET /api/v3/admin/custom_field_definitions
  • GET /api/v3/admin/custom_field_definitions/:id
  • POST /api/v3/admin/custom_field_definitions
  • PATCH /api/v3/admin/custom_field_definitions/:id
  • DELETE /api/v3/admin/custom_field_definitions/:id

6.0: display_onstorefront_visible (boolean). metafield_typefield_type. See 5.4-6.0-custom-fields-rename.md.

Custom Fields (polymorphic, nested under any resource)

  • GET /api/v3/admin/:resource_type/:resource_id/custom_fields
  • POST /api/v3/admin/:resource_type/:resource_id/custom_fields
  • PATCH /api/v3/admin/:resource_type/:resource_id/custom_fields/:id
  • DELETE /api/v3/admin/:resource_type/:resource_id/custom_fields/:id

Translations (polymorphic, nested under translatable resources)

  • GET /api/v3/admin/:resource_type/:resource_id/translations
  • PATCH /api/v3/admin/:resource_type/:resource_id/translations

Tags

  • GET /api/v3/admin/tags
  • POST /api/v3/admin/tags
  • DELETE /api/v3/admin/tags/:id

Refund Reasons

  • GET /api/v3/admin/refund_reasons
  • GET /api/v3/admin/refund_reasons/:id
  • POST /api/v3/admin/refund_reasons
  • PATCH /api/v3/admin/refund_reasons/:id
  • DELETE /api/v3/admin/refund_reasons/:id

Return Reasons (renamed from Return Authorization Reasons)

  • GET /api/v3/admin/return_reasons
  • GET /api/v3/admin/return_reasons/:id
  • POST /api/v3/admin/return_reasons
  • PATCH /api/v3/admin/return_reasons/:id
  • DELETE /api/v3/admin/return_reasons/:id

Store Credit Categories

  • GET /api/v3/admin/store_credit_categories
  • GET /api/v3/admin/store_credit_categories/:id
  • POST /api/v3/admin/store_credit_categories
  • PATCH /api/v3/admin/store_credit_categories/:id
  • DELETE /api/v3/admin/store_credit_categories/:id

Webhook Endpoints

  • GET /api/v3/admin/webhook_endpoints
  • GET /api/v3/admin/webhook_endpoints/:id
  • POST /api/v3/admin/webhook_endpoints
  • PATCH /api/v3/admin/webhook_endpoints/:id
  • DELETE /api/v3/admin/webhook_endpoints/:id

Webhook Deliveries (nested under Webhook Endpoints, read-only)

  • GET /api/v3/admin/webhook_endpoints/:webhook_endpoint_id/deliveries
  • GET /api/v3/admin/webhook_endpoints/:webhook_endpoint_id/deliveries/:id

API Keys

  • GET /api/v3/admin/api_keys
  • POST /api/v3/admin/api_keys
  • PATCH /api/v3/admin/api_keys/:id
  • DELETE /api/v3/admin/api_keys/:id
  • PATCH /api/v3/admin/api_keys/:id/revoke

Integrations

  • GET /api/v3/admin/integrations
  • GET /api/v3/admin/integrations/:id
  • POST /api/v3/admin/integrations
  • PATCH /api/v3/admin/integrations/:id
  • DELETE /api/v3/admin/integrations/:id

6.0: Integration records back the TaxProvider, DeliveryRateProvider, FulfillmentProvider, and PickupPointProvider interfaces (along with external search, email, etc.).

Reports

  • GET /api/v3/admin/reports
  • GET /api/v3/admin/reports/:id
  • POST /api/v3/admin/reports — generate

Exports

  • GET /api/v3/admin/exports
  • GET /api/v3/admin/exports/:id — includes download URL
  • POST /api/v3/admin/exports

Imports

  • GET /api/v3/admin/imports
  • GET /api/v3/admin/imports/:id — status, row counts
  • POST /api/v3/admin/imports — multipart/form-data
  • PATCH /api/v3/admin/imports/:id/complete_mapping

Dashboard

  • GET /api/v3/admin/dashboard/analytics

Open Questions

  • Bulk operations envelope — do we want a single POST /bulk endpoint per resource, or per-action bulk routes (bulk_update, bulk_destroy)? Currently leaning toward per-action for clarity, but it bloats the route table.
  • Order user assignment — is PUT /orders/:id/user the right shape, or should this be PATCH /orders/:id { customer_id: ... }?
  • Polymorphic custom fields routing/:resource_type/:resource_id/custom_fields is ugly. Consider a flat /custom_fields?resource_type=...&resource_id=... instead.
  • Translations endpoint — one PATCH for all locales vs. one PATCH per locale?

References

  • docs/api-reference/store-api/errors.mdx — error response conventions (shared)
  • docs/plans/6.0-admin-spa.md — consumer of this API
  • docs/plans/6.0-platform-auth.md — auth stack and Spree.admin_user_class rename
  • docs/plans/5.4-6.0-custom-fields-rename.md — Metafields → Custom Fields
  • docs/plans/6.0-fulfillment-and-delivery.md — shipping category dropped