dist/codex/spree-commerce/skills/spree-api-v3/SKILL.md
Build with Spree's v3 APIs (v5.4+) — Store API at `/api/v3/store/*` (publishable key + per-user JWT, customer-facing) and Admin API at `/api/v3/admin/*` (per-user API keys + OAuth2 Doorkeeper, admin/operations). Covers the flat-JSON Stripe-like envelope, `?expand=` / `?include=` parameters, prefixed IDs (`prod_…`, `ord_…`), OpenAPI 3.0 spec, rate limiting, idempotency, and migration from v2 JSON:API. Use when building API clients, SDKs, or extending API endpoints in a Spree v5.4+ deployment.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins spree-api-v3Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
app/controllers/spree/api/v3/ for the canonical request/response shape per endpoint.config/initializers/spree.rb for the deployment you're targeting.| API | Path | Auth | Audience |
|-----|------|------|----------|
| Store API | /api/v3/store/* | Publishable key (pk_…) + per-user JWT bearer | Customers, storefronts |
| Admin API | /api/v3/admin/* | Per-user API keys + OAuth2 (Doorkeeper) | Admin, operations, server-to-server |
Both follow the same envelope conventions.
Flat JSON (Stripe-like), not JSON:API:
{
"id": "prod_01HXVZ...",
"object": "product",
"name": "Classic Tee",
"description": "100% cotton",
"created_at": "2026-04-15T10:00:00Z",
"variants": ["var_01HXVZ...", "var_01HXVZ..."],
"default_variant": {
"id": "var_01HXVZ...",
"object": "variant",
"sku": "TEE-S",
"price": "19.99",
"currency": "USD"
}
}
vs. v2's JSON:API:
{ "data": { "id": "1", "type": "product", "attributes": { ... }, "relationships": { ... } } }
IDs are strings with type prefixes:
| Prefix | Type |
|--------|------|
| prod_ | Product |
| var_ | Variant |
| ord_ | Order |
| usr_ | User |
| pay_ | Payment |
| ship_ | Shipment |
| cust_ | Customer |
| pm_ | PaymentMethod |
| sm_ | ShippingMethod |
| txn_ | Transaction |
| prom_ | Promotion |
Prefixed IDs are stable — never reused, safe to expose, decoupled from internal database IDs. Verify the full prefix table in the live docs.
GET /api/v3/store/products?limit=25&starting_after=prod_…&ending_before=prod_…
Returns:
{
"object": "list",
"url": "/api/v3/store/products",
"has_more": true,
"data": [ { "id": "prod_…", ... }, ... ]
}
Cursor pagination via starting_after / ending_before (verify against live spec — may be page-based on some endpoints).
GET /api/v3/store/orders/ord_…?expand=line_items,line_items.variant,shipping_address
expand inlines nested objects; include (also supported) returns them in a separate included array. Verify which convention the version you're on uses.
Per-endpoint filter params:
GET /api/v3/admin/orders?status=complete&payment_state=paid&created_after=2026-01-01
Verify the filter set per endpoint in the OpenAPI spec.
Two layers:
pk_…) — identifies the store + currency. Required on every request via Authorization: Bearer pk_… or X-Spree-Token header (verify current)./api/v3/store/account/sign_in, sent in Authorization: Bearer <jwt>. Identifies the logged-in user.For guest carts, the cart's order_token is sent in X-Spree-Order-Token.
Two paths:
Authorization: Bearer <api_key>. Inherits the user's roles.client_credentials grant against POST /spree_oauth/token returns an access token with admin scope.Per-endpoint, per-token. Defaults set in config/initializers/spree.rb. Headers returned:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1715000000
Verify the exact header names against the live source.
For mutating endpoints (POST/PUT), send Idempotency-Key: <uuid> to ensure retries don't double-create. Spree caches the response for 24+ hours (verify current TTL).
{
"object": "error",
"type": "validation_error",
"code": "invalid_email",
"message": "Email is not valid",
"param": "email",
"request_id": "req_…"
}
HTTP status codes follow standard REST: 400 / 401 / 403 / 404 / 422 / 429 / 500.
Webhooks 2.0 fire alongside API operations — see spree-events-webhooks skill.
In Spree admin → Settings → API Keys:
pk_live_… or pk_test_…) per storeNEXT_PUBLIC_SPREE_PUBLISHABLE_KEY env varPOST /api/v3/store/account/sign_in
Authorization: Bearer pk_live_…
Content-Type: application/json
{ "email": "[email protected]", "password": "..." }
Response:
{
"object": "auth_token",
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "..."
}
Store in httpOnly cookie (server-side); never expose to the browser.
POST /api/v3/store/cart
Authorization: Bearer pk_live_…
Response includes order_token — store in httpOnly cookie. Subsequent cart calls use X-Spree-Order-Token.
GET /api/v3/store/products?expand=default_variant,images&limit=20
Authorization: Bearer pk_live_…
PATCH /api/v3/admin/orders/ord_…
Authorization: Bearer <admin_api_key>
Idempotency-Key: <uuid>
Content-Type: application/json
{ "internal_note": "Customer requested expedited shipping" }
POST /spree_oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=...&client_secret=...&scope=admin
Response:
{ "access_token": "...", "token_type": "Bearer", "expires_in": 7200, "scope": "admin" }
Field shape changes:
data.attributes.name → namedata.attributes.created-at → created_atdata.relationships.variants.data → variants (array of prefixed IDs by default; expand=variants to inline)data.id (numeric string) → prefixed IDYou can run v2 and v3 side-by-side during migration via the spree_legacy_api_v2 gem.
npx @openapitools/openapi-generator-cli generate \
-i https://your-spree.com/api/v3/openapi.json \
-g typescript-axios \
-o ./generated-client
But prefer @spree/sdk if you don't need every endpoint — see the spree-typescript-sdk skill.
/api/v2/storefront/cart is the legacy path; v3 is /api/v3/store/cart.Always re-fetch the OpenAPI spec for the version you're on — field shapes can shift between minor releases.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.