Spree Commerce Authentication API
Admin user login, logout, token refresh, and current user profile
Admin user login, logout, token refresh, and current user profile
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": "<your_key>", ... }` 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": "<your_key>", ... }`
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: '<api-key>',\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: '<api-key>',\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: '<api-key>',\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: '<api-key>',\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: '<api-key>',\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: Produ
# --- truncated at 32 KB (32 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/spree/refs/heads/main/openapi/spree-authentication-api-openapi.yml