openapi: 3.0.3 info: title: Admin Account / Address Authentication API contact: name: Spree Commerce url: https://spreecommerce.org email: hello@spreecommerce.org description: "Spree Admin API v3 - Administrative API for managing products, orders, and store settings.\n\n## Authentication\n\nThe Admin API requires a secret API key passed in the `x-spree-api-key` header.\nSecret API keys can be generated in the Spree admin dashboard.\n\n## Response Format\n\nAll responses are JSON. List endpoints return paginated responses with `data` and `meta` keys.\nSingle resource endpoints return a flat JSON object.\n\n## Resource IDs\n\nEvery resource is identified by an opaque string ID (e.g. `prod_86Rf07xd4z`,\n`variant_k5nR8xLq`, `or_UkLWZg9DAJ`). Use these IDs everywhere — URL paths,\nrequest bodies, and Ransack filters all accept them directly.\n\n## Error Handling\n\nErrors return a consistent format:\n```json\n{\n \"error\": {\n \"code\": \"validation_error\",\n \"message\": \"Validation failed\",\n \"details\": { \"name\": [\"can't be blank\"] }\n }\n}\n```\n" version: v3 servers: - url: http://{defaultHost} variables: defaultHost: default: localhost:3000 tags: - name: Authentication description: Admin user login, logout, token refresh, and current user profile paths: /api/v3/admin/auth/login: post: summary: Login tags: - Authentication security: - api_key: [] description: '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. ' x-codeSamples: - lang: javascript label: Spree Admin SDK source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\n// The refresh token is set as an HttpOnly cookie; only `token` and `user` come back in the body.\nconst auth = await client.auth.login({\n email: 'admin@example.com',\n password: 'password123',\n})" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '200': description: login successful content: application/json: example: token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX3R5cGUiOiJhZG1pbiIsImp0aSI6ImUyMzYxNTc5LWMxZDMtNDc0Yi1hM2E1LTcwNWMzYTZmMGUxYSIsImlzcyI6InNwcmVlIiwiYXVkIjoiYWRtaW5fYXBpIiwiZXhwIjoxNzgxMjg1MzI3fQ.5GaxxoaCeEEpGgum01ZV0z56ZpHDWaFSyX_KvEob3ws user: id: admin_UkLWZg9DAJ email: admin@example.com first_name: Lisandra last_name: Dare full_name: Lisandra Dare created_at: '2026-06-12T17:23:47.304Z' updated_at: '2026-06-12T17:23:47.304Z' roles: - id: role_UkLWZg9DAJ name: admin schema: $ref: '#/components/schemas/AuthResponse' '401': description: invalid credentials content: application/json: example: error: code: authentication_failed message: Invalid email or password schema: $ref: '#/components/schemas/ErrorResponse' requestBody: content: application/json: schema: oneOf: - title: EmailPasswordLogin description: Built-in email/password authentication (default when `provider` is omitted). type: object properties: provider: type: string enum: - email default: email email: type: string format: email example: admin@example.com password: type: string example: password123 required: - email - password - title: ProviderLogin description: 'Provider-dispatched login. The `provider` key selects a registered strategy class; the remaining fields are forwarded to the strategy''s `authenticate` method. Required fields depend on the registered strategy — consult its documentation. ' type: object properties: provider: type: string example: okta description: Registered provider key (anything other than `email`). not: enum: - email required: - provider additionalProperties: true /api/v3/admin/auth/refresh: post: summary: Refresh token tags: - Authentication security: - api_key: [] description: '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. ' x-codeSamples: - lang: javascript label: Spree Admin SDK source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\n// Driven entirely by the HttpOnly refresh-token cookie + CSRF header (set by the SDK).\nconst auth = await client.auth.refresh()" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '200': description: refresh successful content: application/json: example: token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX3R5cGUiOiJhZG1pbiIsImp0aSI6IjYxNTkwZmYyLWEwMTctNDUyZi05ZjQ3LTBhM2JhYWZlYWQ0YiIsImlzcyI6InNwcmVlIiwiYXVkIjoiYWRtaW5fYXBpIiwiZXhwIjoxNzgxMjg1MzI4fQ.l53NCGqm7_mD8L54Wq7AB86HGHqolGJEplh7Dg_QqJw user: id: admin_UkLWZg9DAJ email: admin@example.com first_name: Lavonda last_name: Bogan full_name: Lavonda Bogan created_at: '2026-06-12T17:23:48.682Z' updated_at: '2026-06-12T17:23:48.682Z' roles: - id: role_UkLWZg9DAJ name: admin schema: $ref: '#/components/schemas/AuthResponse' '401': description: missing or invalid refresh-token cookie content: application/json: example: error: code: invalid_refresh_token message: Refresh token cookie missing schema: $ref: '#/components/schemas/ErrorResponse' /api/v3/admin/auth/logout: post: summary: Logout tags: - Authentication security: - api_key: [] description: Revokes the refresh-token cookie, effectively logging the admin out. x-codeSamples: - lang: javascript label: Spree Admin SDK source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nawait client.auth.logout()" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '204': description: logout successful /api/v3/admin/me: get: summary: Get current admin user and permissions tags: - Authentication security: - api_key: [] bearer_auth: [] description: Returns the current admin user profile and a serialized list of permissions (CanCanCan rules). The SPA uses these to drive UI permission checks. x-codeSamples: - lang: javascript label: Spree Admin SDK source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst me = await client.me.get()\nif (me.permissions.some((r) => r.allow && r.actions.includes('manage') && r.subjects.includes('Spree::Product'))) {\n // show \"Create product\" button\n}" parameters: - name: x-spree-api-key in: header required: true schema: type: string - name: Authorization in: header required: true description: Bearer token for admin authentication schema: type: string responses: '200': description: current admin user and permissions content: application/json: example: user: id: admin_UkLWZg9DAJ email: loralee@bodeolson.name first_name: Thelma last_name: Cronin full_name: Thelma Cronin created_at: '2026-06-12T17:24:22.538Z' updated_at: '2026-06-12T17:24:22.538Z' roles: - id: role_UkLWZg9DAJ name: admin permissions: - allow: true actions: - manage subjects: - all has_conditions: false - allow: false actions: - cancel subjects: - Spree::Order has_conditions: false - allow: true actions: - cancel subjects: - Spree::Order has_conditions: true - allow: false actions: - destroy subjects: - Spree::Order has_conditions: false - allow: true actions: - destroy subjects: - Spree::Order has_conditions: true - allow: false actions: - edit - update subjects: - Spree::RefundReason has_conditions: true - allow: false actions: - edit - update subjects: - Spree::ReimbursementType has_conditions: true - allow: false actions: - update - destroy subjects: - Spree::Role has_conditions: true schema: $ref: '#/components/schemas/MeResponse' '401': description: unauthorized content: application/json: example: error: code: authentication_required message: Authentication required schema: $ref: '#/components/schemas/ErrorResponse' /api/v3/store/auth/login: post: summary: Login tags: - Authentication security: - api_key: [] description: '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. ' x-codeSamples: - lang: javascript label: Spree SDK source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n baseUrl: 'https://your-store.com',\n publishableKey: '',\n})\n\nconst auth = await client.auth.login({\n email: 'customer@example.com',\n password: 'password123',\n})" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '200': description: login successful content: application/json: example: token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX3R5cGUiOiJjdXN0b21lciIsImp0aSI6IjY0NjcwM2Q0LWY5ZjAtNDlmMi05ZmZkLTA3YTZhY2I5YWZkZiIsImlzcyI6InNwcmVlIiwiYXVkIjoic3RvcmVfYXBpIiwiZXhwIjoxNzc5ODE1NjE1fQ.jlz2KHxYkB1Dd9ucl26zy6E5M7dFB5q9g-Qw0YjsX50 refresh_token: MQ9QZ1ToR8QocZoDd4ggC8yN user: id: cus_UkLWZg9DAJ email: test@example.com first_name: Colette last_name: Hegmann phone: null accepts_email_marketing: false full_name: Colette Hegmann available_store_credit_total: '0' display_available_store_credit_total: $0.00 addresses: [] default_billing_address: null default_shipping_address: null schema: $ref: '#/components/schemas/AuthResponse_2' '401': description: missing API key content: application/json: example: error: code: invalid_token message: Valid API key required schema: $ref: '#/components/schemas/ErrorResponse' requestBody: content: application/json: schema: oneOf: - title: EmailPasswordLogin description: Built-in email/password authentication (default when `provider` is omitted). type: object properties: provider: type: string enum: - email default: email email: type: string format: email example: customer@example.com password: type: string example: password123 required: - email - password - title: ProviderLogin description: 'Provider-dispatched login. The `provider` key selects a registered strategy class; the remaining fields are forwarded to the strategy''s `authenticate` method. Required fields depend on the registered strategy — consult its documentation. ' type: object properties: provider: type: string example: auth0 description: Registered provider key (anything other than `email`). not: enum: - email required: - provider additionalProperties: true /api/v3/store/auth/refresh: post: summary: Refresh token tags: - Authentication security: - api_key: [] description: Exchanges a refresh token for a new access JWT and rotated refresh token. No Authorization header needed. x-codeSamples: - lang: javascript label: Spree SDK source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n baseUrl: 'https://your-store.com',\n publishableKey: '',\n})\n\nconst auth = await client.auth.refresh({\n refresh_token: 'rt_xxx',\n})" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '200': description: token refreshed content: application/json: example: token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX3R5cGUiOiJjdXN0b21lciIsImp0aSI6IjNmZjU3NWU0LTVmNmItNDVjOC04MzIzLTIzMDMyMzVjZTkzMiIsImlzcyI6InNwcmVlIiwiYXVkIjoic3RvcmVfYXBpIiwiZXhwIjoxNzc5ODE1NjE2fQ.OuCk-UNe-asA8DvK2yKMkp94BQz9PN_Z7_SnReeIRYE refresh_token: qLuZDRo8LqywXFThqPM5V2Ug user: id: cus_UkLWZg9DAJ email: test@example.com first_name: Debi last_name: Tillman phone: null accepts_email_marketing: false full_name: Debi Tillman available_store_credit_total: '0' display_available_store_credit_total: $0.00 addresses: [] default_billing_address: null default_shipping_address: null schema: $ref: '#/components/schemas/AuthResponse_2' '401': description: missing or invalid refresh token content: application/json: example: error: code: invalid_refresh_token message: Invalid or expired refresh token schema: $ref: '#/components/schemas/ErrorResponse' requestBody: content: application/json: schema: type: object properties: refresh_token: type: string description: Refresh token from login response required: - refresh_token /api/v3/store/auth/logout: post: summary: Logout tags: - Authentication security: - api_key: [] description: 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. x-codeSamples: - lang: javascript label: Spree SDK source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n baseUrl: 'https://your-store.com',\n publishableKey: '',\n})\n\nawait client.auth.logout({\n refresh_token: 'rt_xxx',\n})" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '204': description: logout without refresh token (no-op) requestBody: content: application/json: schema: type: object properties: refresh_token: type: string description: Refresh token to revoke /api/v3/store/password_resets: post: summary: Request a password reset tags: - Authentication security: - api_key: [] description: Sends a password reset email if an account exists for the given email address. Always returns 202 Accepted to prevent email enumeration. x-codeSamples: - lang: javascript label: Spree SDK source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n baseUrl: 'https://your-store.com',\n publishableKey: '',\n})\n\nawait client.passwordResets.create({\n email: 'customer@example.com',\n redirect_url: 'https://myshop.com/reset-password',\n})" parameters: - name: x-spree-api-key in: header required: true schema: type: string responses: '202': description: email not found (same response to prevent enumeration) content: application/json: example: message: If an account exists for that email, password reset instructions have been sent. schema: type: object properties: message: type: string '401': description: missing API key content: application/json: example: error: code: invalid_token message: Valid API key required schema: $ref: '#/components/schemas/ErrorResponse' requestBody: content: application/json: schema: type: object properties: email: type: string format: email example: customer@example.com description: Email address of the account to reset redirect_url: type: string format: uri example: https://myshop.com/reset-password description: URL to redirect the user to after clicking the reset link. Validated against the store's allowed origins. required: - email /api/v3/store/password_resets/{token}: patch: summary: Reset password with token tags: - Authentication security: - api_key: [] description: Resets the password using a token received via email. Returns a JWT token on success (auto-login). x-codeSamples: - lang: javascript label: Spree SDK source: "import { createClient } from '@spree/sdk'\n\nconst client = createClient({\n baseUrl: 'https://your-store.com',\n publishableKey: '',\n})\n\nconst auth = await client.passwordResets.update(\n 'reset-token-from-email',\n {\n password: 'newsecurepassword',\n password_confirmation: 'newsecurepassword',\n }\n)" parameters: - name: x-spree-api-key in: header required: true schema: type: string - name: token in: path required: true description: Password reset token from the email schema: type: string responses: '200': description: password reset successful content: application/json: example: token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX3R5cGUiOiJjdXN0b21lciIsImp0aSI6IjU1YjA5Yzc4LTUzMTYtNDRjYy05ZjJiLThmN2E2MzViMmRlNSIsImlzcyI6InNwcmVlIiwiYXVkIjoic3RvcmVfYXBpIiwiZXhwIjoxNzc5ODE1NjI4fQ.FX2Te4WfdAu3kN_fvvfHsH92_axvIZTI1d8Zmw8R1mE refresh_token: Be2HUCjiJVRjgidFuYU7GRRh user: id: cus_UkLWZg9DAJ email: customer@example.com first_name: Randa last_name: O'Hara phone: null accepts_email_marketing: false full_name: Randa O'Hara available_store_credit_total: '0' display_available_store_credit_total: $0.00 addresses: [] default_billing_address: null default_shipping_address: null schema: $ref: '#/components/schemas/AuthResponse_2' '422': description: password confirmation mismatch content: application/json: example: error: code: validation_error message: Password confirmation doesn't match Password details: password_confirmation: - doesn't match Password schema: $ref: '#/components/schemas/ErrorResponse' '401': description: missing API key content: application/json: example: error: code: invalid_token message: Valid API key required schema: $ref: '#/components/schemas/ErrorResponse' requestBody: content: application/json: schema: type: object properties: password: type: string minLength: 6 example: newsecurepassword password_confirmation: type: string example: newsecurepassword required: - password - password_confirmation components: schemas: AuthResponse: type: object properties: token: type: string description: JWT access token user: $ref: '#/components/schemas/AdminUser' required: - token - user AdminUserRoleAssignment: type: object description: A role assignment for the current store on a staff member properties: id: type: string description: Prefixed role ID example: role_abc123 name: type: string description: Role name example: admin required: - id - name AdminUser: type: object properties: id: type: string email: type: string first_name: type: string nullable: true last_name: type: string nullable: true full_name: type: string nullable: true created_at: type: string updated_at: type: string roles: type: array items: $ref: '#/components/schemas/AdminUserRoleAssignment' required: - id - email - first_name - last_name - full_name - created_at - updated_at - roles x-typelizer: true Customer: type: object properties: id: type: string email: type: string first_name: type: string nullable: true last_name: type: string nullable: true phone: type: string nullable: true accepts_email_marketing: type: boolean full_name: type: string available_store_credit_total: type: string display_available_store_credit_total: type: string addresses: type: array items: $ref: '#/components/schemas/Address' default_billing_address: allOf: - $ref: '#/components/schemas/Address' nullable: true default_shipping_address: allOf: - $ref: '#/components/schemas/Address' nullable: true required: - id - email - first_name - last_name - phone - accepts_email_marketing - full_name - available_store_credit_total - display_available_store_credit_total - addresses - default_billing_address - default_shipping_address x-typelizer: true Address: type: object properties: id: type: string first_name: type: string nullable: true last_name: type: string nullable: true full_name: type: string address1: type: string nullable: true address2: type: string nullable: true postal_code: type: string nullable: true city: type: string nullable: true phone: type: string nullable: true company: type: string nullable: true country_name: type: string country_iso: type: string state_text: type: string nullable: true state_abbr: type: string nullable: true quick_checkout: type: boolean is_default_billing: type: boolean is_default_shipping: type: boolean state_name: type: string nullable: true required: - id - first_name - last_name - full_name - address1 - address2 - postal_code - city - phone - company - country_name - country_iso - state_text - state_abbr - quick_checkout - is_default_billing - is_default_shipping - state_name x-typelizer: true PermissionRule: type: object description: A single permission rule (CanCanCan rule). Rules are applied in order, last-matching-wins. properties: allow: type: boolean description: true for `can`, false for `cannot` actions: type: array items: type: string description: Action names, e.g. ["read", "update"] or ["manage"] subjects: type: array items: type: string description: Subject class names, e.g. ["Spree::Product"] or ["all"] has_conditions: type: boolean description: True if the server-side rule has per-record conditions. The SPA shows the action optimistically and handles 403 from the API. required: - allow - actions - subjects - has_conditions ErrorResponse: type: object properties: error: type: object properties: code: type: string example: record_not_found message: type: string example: Record not found details: type: object description: Field-specific validation errors nullable: true example: name: - is too short - is required email: - is invalid required: - code - message required: - error example: error: code: validation_error message: Validation failed details: name: - is too short email: - is invalid MeResponse: type: object description: Current admin user profile and serialized permissions properties: user: $ref: '#/components/schemas/AdminUser' permissions: type: array items: $ref: '#/components/schemas/PermissionRule' required: - user - permissions AuthResponse_2: type: object properties: token: type: string description: JWT access token refresh_token: type: string description: Refresh token for obtaining new access tokens user: $ref: '#/components/schemas/Customer' required: - token - refresh_token - user securitySchemes: api_key: type: apiKey name: x-spree-api-key in: header description: Secret API key for admin access bearer_auth: type: http scheme: bearer bearerFormat: JWT description: JWT token for admin user authentication x-tagGroups: - name: Authentication tags: - Authentication - name: Products & Catalog tags: - Products - Variants - Option Types - Custom Fields - Channels - name: Pricing tags: - Pricing - Markets - name: Orders & Fulfillment tags: - Orders - Payments - Fulfillments - Refunds - name: Customers tags: - Customers - Customer Groups - name: Promotions & Gift Cards tags: - Promotions - Gift Cards - name: Data tags: - Exports - name: Configuration tags: - Settings - Stock Locations - Payment Methods - Staff - API Keys - Allowed Origins - Webhooks