Spree Commerce Customer Groups API
Customer groups for segmenting customers (e.g. wholesale, VIP) used by pricing and promotions
Customer groups for segmenting customers (e.g. wholesale, VIP) used by pricing and promotions
openapi: 3.0.3
info:
title: Admin Account / Address Customer Groups 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: Customer Groups
description: Customer groups for segmenting customers (e.g. wholesale, VIP) used by pricing and promotions
paths:
/api/v3/admin/customer_groups:
get:
summary: List customer groups
tags:
- Customer Groups
security:
- api_key: []
bearer_auth: []
description: 'Returns the customer groups configured for the current store. Groups
segment customers for targeted promotions (see the `customer_group`
promotion rule) and reporting. The list endpoint never embeds the
member list — fetch a single group with `?expand=customers` if you
need them inline, or query `/admin/customers?customer_group_id_in=…`
for paginated membership.
**Required scope:** `read_customers` (for API-key authentication).'
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 { data: groups } = await client.customerGroups.list({ page: 1, limit: 25 })"
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
- name: page
in: query
required: false
description: Page number
schema:
type: integer
- name: limit
in: query
required: false
description: Number of records per page
schema:
type: integer
- name: q[name_cont]
in: query
required: false
description: Filter by name (contains)
schema:
type: string
- name: sort
in: query
required: false
description: Sort by field. Prefix with `-` for descending (e.g., `-created_at`).
schema:
type: string
- name: fields
in: query
required: false
description: Comma-separated list of fields to include. id is always included.
schema:
type: string
responses:
'200':
description: customer groups found
content:
application/json:
example:
data:
- id: cg_UkLWZg9DAJ
name: VIPs
description: Top spenders
customers_count: 0
created_at: '2026-06-12T17:23:54.579Z'
updated_at: '2026-06-12T17:23:54.579Z'
- id: cg_gbHJdmfrXB
name: Wholesale
description: null
customers_count: 0
created_at: '2026-06-12T17:23:54.580Z'
updated_at: '2026-06-12T17:23:54.580Z'
meta:
page: 1
limit: 25
count: 2
pages: 1
from: 1
to: 2
in: 2
previous: null
next: null
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/CustomerGroup'
meta:
$ref: '#/components/schemas/PaginationMeta'
required:
- data
- meta
'401':
description: unauthorized
content:
application/json:
example:
error:
code: authentication_required
message: Authentication required
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Create a customer group
tags:
- Customer Groups
security:
- api_key: []
bearer_auth: []
description: 'Creates a customer group in the current store. `customer_ids` is
optional; when present, customers are attached at create time.
Pass prefixed IDs (e.g. `cus_…`) — the server decodes them
automatically.
**Required scope:** `write_customers` (for API-key authentication).'
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 group = await client.customerGroups.create({\n name: 'VIP customers',\n description: 'Top spenders, eligible for early access',\n customer_ids: ['cus_UkLWZg9DAJ', 'cus_QrLWXg9CAJ'],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'201':
description: customer group created
content:
application/json:
example:
id: cg_EfhxLZ9ck8
name: Wholesale 2
description: B2B accounts
customers_count: 1
created_at: '2026-06-12T17:23:55.564Z'
updated_at: '2026-06-12T17:23:55.564Z'
schema:
$ref: '#/components/schemas/CustomerGroup'
'422':
description: validation error
content:
application/json:
example:
error:
code: validation_error
message: Name can't be blank
details:
name:
- can't be blank
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: Wholesale
description:
type: string
example: B2B accounts
nullable: true
customer_ids:
type: array
items:
type: string
example:
- cus_UkLWZg9DAJ
/api/v3/admin/customer_groups/{id}:
parameters:
- name: id
in: path
required: true
description: Customer group prefixed ID
schema:
type: string
get:
summary: Get a customer group
tags:
- Customer Groups
security:
- api_key: []
bearer_auth: []
description: 'Returns a single customer group. Pass `?expand=customers` to embed
the full member list inline (recommended only for single-record
reads — embed cost scales with membership size).
**Required scope:** `read_customers` (for API-key authentication).'
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// Pass `expand: ['customers']` to embed the group's customers in the response\n// — omit it for the much smaller index-payload shape.\nconst group = await client.customerGroups.get('cg_UkLWZg9DAJ', { expand: ['customers'] })"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: expand
in: query
required: false
description: 'Comma-separated associations to embed. Supported: `customers`.'
schema:
type: string
- name: fields
in: query
required: false
description: Comma-separated list of fields to include. id is always included.
schema:
type: string
responses:
'200':
description: customer group with embedded customers
content:
application/json:
example:
id: cg_UkLWZg9DAJ
name: VIPs
description: Top spenders
customers_count: 1
created_at: '2026-06-12T17:23:56.255Z'
updated_at: '2026-06-12T17:23:56.255Z'
customers:
- id: cus_UkLWZg9DAJ
email: edra@mitchell.biz
first_name: Karly
last_name: Labadie
phone: null
accepts_email_marketing: false
full_name: Karly Labadie
available_store_credit_total: '0'
display_available_store_credit_total: $0.00
login: edra@mitchell.biz
metadata: {}
last_sign_in_at: null
current_sign_in_at: null
created_at: '2026-06-12T17:23:56.566Z'
updated_at: '2026-06-12T17:23:56.566Z'
sign_in_count: 0
failed_attempts: 0
last_sign_in_ip: null
current_sign_in_ip: null
tags: []
internal_note_html: null
default_billing_address_id: null
default_shipping_address_id: null
orders_count: 0
total_spent: '0.0'
display_total_spent: $0.00
last_order_completed_at: null
schema:
$ref: '#/components/schemas/CustomerGroup'
'404':
description: customer group not found
content:
application/json:
example:
error:
code: record_not_found
message: Customer group not found
schema:
$ref: '#/components/schemas/ErrorResponse'
patch:
summary: Update a customer group
tags:
- Customer Groups
security:
- api_key: []
bearer_auth: []
description: 'Updates name, description, or membership. `customer_ids` is a
full-set replacement — the server reconciles the membership to
match the array, adding new IDs and removing ones not present.
Send `customer_ids: []` to clear all members.
**Required scope:** `write_customers` (for API-key authentication).'
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// `customer_ids` is a full-set update — the model reconciles the membership\n// to match the array (adds new IDs, removes ones not present).\nconst group = await client.customerGroups.update('cg_UkLWZg9DAJ', {\n name: 'VIP customers (Q1)',\n customer_ids: ['cus_UkLWZg9DAJ'],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: customer group updated
content:
application/json:
example:
id: cg_UkLWZg9DAJ
name: VIP customers (Q1)
description: Top spenders
customers_count: 0
created_at: '2026-06-12T17:23:57.311Z'
updated_at: '2026-06-12T17:23:57.647Z'
schema:
$ref: '#/components/schemas/CustomerGroup'
'422':
description: validation error
content:
application/json:
example:
error:
code: validation_error
message: Name can't be blank
details:
name:
- can't be blank
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: VIP customers (Q1)
description:
type: string
example: Updated description
nullable: true
customer_ids:
type: array
items:
type: string
example:
- cus_UkLWZg9DAJ
delete:
summary: Delete a customer group
tags:
- Customer Groups
security:
- api_key: []
bearer_auth: []
description: 'Soft-deletes the group. Member users are not deleted; their `customer_group_users` rows are dropped.
**Required scope:** `write_customers` (for API-key authentication).'
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.customerGroups.delete('cg_UkLWZg9DAJ')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'204':
description: customer group deleted
components:
schemas:
LineItem:
type: object
properties:
id:
type: string
variant_id:
type: string
quantity:
type: number
currency:
type: string
name:
type: string
slug:
type: string
options_text:
type: string
price:
type: string
display_price:
type: string
total:
type: string
display_total:
type: string
adjustment_total:
type: string
display_adjustment_total:
type: string
additional_tax_total:
type: string
display_additional_tax_total:
type: string
included_tax_total:
type: string
display_included_tax_total:
type: string
discount_total:
type: string
display_discount_total:
type: string
pre_tax_amount:
type: string
display_pre_tax_amount:
type: string
discounted_amount:
type: string
display_discounted_amount:
type: string
display_compare_at_amount:
type: string
nullable: true
compare_at_amount:
type: string
nullable: true
thumbnail_url:
type: string
nullable: true
option_values:
type: array
items:
$ref: '#/components/schemas/OptionValue'
digital_links:
type: array
items:
$ref: '#/components/schemas/DigitalLink'
metadata:
type: object
created_at:
type: string
updated_at:
type: string
cost_price:
type: string
nullable: true
tax_category_id:
type: string
nullable: true
variant:
$ref: '#/components/schemas/Variant'
tax_category:
$ref: '#/components/schemas/TaxCategory'
adjustments:
type: array
items:
$ref: '#/components/schemas/Adjustment'
required:
- id
- variant_id
- quantity
- currency
- name
- slug
- options_text
- price
- display_price
- total
- display_total
- adjustment_total
- display_adjustment_total
- additional_tax_total
- display_additional_tax_total
- included_tax_total
- display_included_tax_total
- discount_total
- display_discount_total
- pre_tax_amount
- display_pre_tax_amount
- discounted_amount
- display_discounted_amount
- display_compare_at_amount
- compare_at_amount
- thumbnail_url
- option_values
- digital_links
- metadata
- created_at
- updated_at
- cost_price
- tax_category_id
x-typelizer: true
ReturnAuthorization:
type: object
properties:
id:
type: string
number:
type: string
status:
type: string
order_id:
type: string
nullable: true
stock_location_id:
type: string
nullable: true
return_authorization_reason_id:
type: string
nullable: true
created_at:
type: string
updated_at:
type: string
order:
$ref: '#/components/schemas/Order'
stock_location:
$ref: '#/components/schemas/StockLocation'
required:
- id
- number
- status
- order_id
- stock_location_id
- return_authorization_reason_id
- created_at
- updated_at
x-typelizer: true
StockItem:
type: object
properties:
id:
type: string
count_on_hand:
type: number
backorderable:
type: boolean
stock_location_id:
type: string
nullable: true
variant_id:
type: string
nullable: true
metadata:
type: object
created_at:
type: string
updated_at:
type: string
allocated_count:
type: number
available_count:
type: number
stock_location:
$ref: '#/components/schemas/StockLocation'
variant:
$ref: '#/components/schemas/Variant'
required:
- id
- count_on_hand
- backorderable
- stock_location_id
- variant_id
- metadata
- created_at
- updated_at
- allocated_count
- available_count
x-typelizer: true
PaymentSource:
type: object
properties:
id:
type: string
gateway_payment_profile_id:
type: string
nullable: true
metadata:
type: object
created_at:
type: string
updated_at:
type: string
required:
- id
- gateway_payment_profile_id
- metadata
- created_at
- updated_at
x-typelizer: true
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
Adjustment:
type: object
properties:
id:
type: string
label:
type: string
display_amount:
type: string
included:
type: boolean
created_at:
type: string
updated_at:
type: string
amount:
type: string
order_id:
type: string
nullable: true
required:
- id
- label
- display_amount
- included
- created_at
- updated_at
- amount
- order_id
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
label:
type: string
nullable: true
metadata:
type: object
created_at:
type: string
updated_at:
type: string
customer_id:
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
- label
- metadata
- created_at
- updated_at
- customer_id
x-typelizer: true
DeliveryRate:
type: object
properties:
id:
type: string
delivery_method_id:
type: string
name:
type: string
selected:
type: boolean
cost:
type: string
total:
type: string
additional_tax_total:
type: string
included_tax_total:
type: string
tax_total:
type: string
display_cost:
type: string
display_total:
type: string
display_additional_tax_total:
type: string
display_included_tax_total:
type: string
display_tax_total:
type: string
delivery_method:
$ref: '#/components/schemas/DeliveryMethod'
created_at:
type: string
updated_at:
type: string
required:
- id
- delivery_method_id
- name
- selected
- cost
- total
- additional_tax_total
- included_tax_total
- tax_total
- display_cost
- display_total
- display_additional_tax_total
- display_included_tax_total
- display_tax_total
- created_at
- updated_at
x-typelizer: true
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
FulfillmentManifestItem:
type: object
description: An item within a fulfillment — which line item and how many units are in this fulfillment
properties:
item_id:
type: string
description: Line item ID
example: li_abc123
variant_id:
type: string
description: Variant ID
example: variant_abc123
quantity:
type: integer
description: Quantity in this fulfillment
example: 2
required:
- item_id
- variant_id
- quantity
CustomerGroup:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
nullable: true
customers_count:
type: number
created_at:
type: string
updated_at:
type: string
customers:
type: array
items:
$ref: '#/components/schemas/Customer'
required:
- id
- name
- description
- customers_count
- created_at
- updated_at
x-typelizer: true
CustomField:
type: object
properties:
id:
type: string
label:
type: string
type:
type: string
deprecated: true
field_type:
type: string
enum:
- short_text
- long_text
- rich_text
- number
- boolean
- json
key:
type: string
value:
type: object
created_at:
type: string
updated_at:
type: string
storefront_visible:
type: boolean
custom_field_definition_id:
type: string
required:
- id
- label
- type
- field_type
- key
- value
- created_at
- updated_at
- storefront_visible
- custom_field_definition_id
x-typelizer: true
DeliveryMethod:
type: object
properties:
id:
type: string
name:
type: string
code:
type: string
nullable: true
created_at:
type: string
updated_at:
type: string
required:
- id
- name
- code
- created_at
- updated_at
x-typelizer: true
Fulfillment:
type: object
properties:
id:
type: string
number:
type: string
tracking:
type: string
nullable: true
tracking_url:
type: string
nullable: true
cost:
type: string
display_cost:
type: string
total:
type: string
display_total:
type: string
discount_total:
type: string
display_discount_total:
type: string
additional_tax_total:
type: string
display_additional_tax_total:
type: string
included_tax_total:
type: string
display_included_tax_total:
type: string
tax_total:
type: string
display_tax_total:
type: string
status:
type: string
fulfillment_type:
type: string
fulfilled_at:
type: string
nullable: true
items:
type: array
items:
$ref: '#/components/schemas/FulfillmentManifestItem'
delivery_method:
$ref: '#/components/schemas/DeliveryMethod'
stock_location:
$ref: '#/components/schemas/StockLocation'
delivery_rates:
type: array
items:
$ref: '#/components/schemas/DeliveryRate'
metadata:
type: object
adjustment_total:
type: string
pre_tax_amount:
type: string
created_at:
type: string
updated_at:
type: string
order_id:
type: string
nullable: true
stock_location_id:
type: string
nullable: true
order:
$ref: '#/components/schemas/Order'
adjustments:
type: array
items:
$ref: '#/components/schemas/Adjustment'
required:
- id
- number
- tracking
- tracking_url
- cost
- display_cost
- total
- display_total
- discount_total
- display_discount_total
- additional_tax_total
- display_additional_tax_total
- included_tax_total
- display_included_tax_total
- tax_total
- display_tax_total
- status
- fulfillment_type
- fulfilled_at
- items
- metadata
- adjustment_total
- pre_tax_amount
- created_at
- updated_at
- order_id
- stock_location_id
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
login:
type: string
nullable: true
metadata:
type: object
last_sign_in_at:
type: string
nullable: true
current_sign_in_at:
type: string
nullable: true
created_at:
type: string
updated_at:
type: string
sign_in_count:
type: number
failed_attempts:
type: number
last_sign_in_ip:
type: string
nullable: true
current_sign_in_ip:
type: string
nullable: true
tags:
type: array
items:
type: string
internal_note_html:
type: string
nullable: true
default_billing_address_id:
type: string
nullable: true
default_shipping_address_id:
type: string
nullable: true
orders_count:
type: number
total_spent:
type: string
display_total_spent:
type: string
last_order_completed_at:
type: string
nullable: true
orders:
type: array
items:
$ref: '#/components/schemas/Order'
store
# --- truncated at 32 KB (65 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/spree/refs/heads/main/openapi/spree-customer-groups-api-openapi.yml