Customer Groups require customer scopes: reads use read_customers and writes use write_customers. Manual groups have members you add or remove. Automatic groups select members from their saved rules. A group's type cannot change after creation.
Spending and order-count thresholds#
For lifetime_spend and orders_count, use gte or lte with a non-negative decimal string in value and omit value_id. Read GET /customer-groups/limits before constructing inputs: each numeric operator includes decimal_places and max_value. Spending operators also include currency with the store base currency code, symbol, and display precision. Use decimal_places for accepted amount precision; currency display precision can differ.
Field |
Accepted value |
Meaning |
|---|---|---|
|
Use the store base currency, no grouping or symbol, at most 12 integral digits, and the currency's amount precision. For example, INR accepts |
The customer's total recorded paid value less refunds, in the store base currency. |
|
A whole-number string from |
Orders recorded as paid. Refunding an order does not remove it from this count. For retained purchases that exclude fully refunded orders, use the purchase rules below. |
Create, rule updates, and preview use the same validation. Invalid spending precision or bounds return HTTP 400 GROUP_RULE_MONEY_INVALID; invalid order counts return GROUP_RULE_ORDER_COUNT_INVALID. Malformed numeric values can return GROUP_RULE_NUMBER_INVALID, and unrelated references return GROUP_RULE_VALUE_SHAPE_INVALID. Saved rule values remain decimal strings; General display preferences do not change their meaning. Existing saved thresholds are not rounded automatically. Correct an invalid older threshold before resubmitting its rules.
Use purchase rules#
Automatic Customer Groups can use last_purchase_at and purchased_product rules. Before authoring or previewing one, call GET /customer-groups/purchase-rule-readiness. This read endpoint requires read_customers; the credential determines the store.
{
"success": true,
"data": {
"available": true,
"status": "ready"
}
}
While purchase history is being prepared, the endpoint returns available: false, status: refreshing, and reason: CUSTOMER_PURCHASE_FACTS_REFRESHING. Requests that create, change, preview, or evaluate purchase rules then return HTTP 409 with the same error code. Metadata-only updates, such as changing a group name without submitting rules, remain available. Retry after readiness becomes ready; do not interpret refreshing as no matching customers.
Field |
Operators |
Value and behavior |
|---|---|---|
|
|
For |
|
|
Put the positive parent product ID in |
Purchase conditions count paid and cash-on-delivery orders with item quantity remaining after refunds. Cancelled, test, unpaid non-COD, and fully refunded orders do not count. A full monetary refund excludes the order even when no individual item quantities were marked as refunded. A fully refunded line no longer matches purchased_product; a partially refunded line still matches while eligible quantity remains. last_purchase_at is the latest order creation time among qualifying purchases, so an order can continue to set the date when another eligible item remains after refunds. Once readiness is complete, an eligible customer with no retained purchase matches last_purchase_at is_null and purchased_product excludes.
An invalid, deleted, or other-store parent product returns HTTP 400 with GROUP_RULE_PRODUCT_INVALID. Invalid dates or day ranges return GROUP_RULE_DATE_INVALID or GROUP_RULE_DURATION_INVALID; missing required values return GROUP_RULE_VALUE_REQUIRED. Extra values on an empty check or a value_id on a purchase-date rule return GROUP_RULE_VALUE_SHAPE_INVALID. Purchase-rule readiness only states whether these rules can be evaluated; it does not grant marketing consent.
Synchronize group membership#
Use the membership snapshot and change feed together when your integration needs a durable local copy of a Customer Group's membership. Both endpoints require read_customers. The credential determines the store, and each item contains only a customer UUID. The feed does not include customer profiles, contact details, or marketing-consent state; treat customer UUIDs as customer data and protect them accordingly.
Task |
Endpoint |
First-page query |
|---|---|---|
Read a complete starting membership |
|
Optional |
Replay later membership changes |
|
Optional non-negative |
Take the initial snapshot#
{
"success": true,
"data": {
"group_id": "58ee109f-64f7-4a51-8d48-b0ca62c4236a",
"revision": 42,
"ready": true,
"last_evaluated_at": "2026-09-12T10:00:00Z",
"customer_ids": ["87964951-2890-4b72-bd8f-78625a76c737"],
"has_more": true,
"next_cursor": "opaque"
}
}
Always take this initial snapshot, even if you plan to request changes from after_revision=0: revision 0 can already have members but has no change item for each preexisting membership. Keep the returned revision. If has_more is true, request the next page with cursor as the only query parameter. The cursor pins every snapshot page to the same revision and page limit. If membership changes while you paginate, the API returns CUSTOMER_GROUP_MEMBERSHIP_RESTART_REQUIRED; discard the partial snapshot and restart without a cursor. An automatic group that is still refreshing returns CUSTOMER_GROUP_MEMBERSHIP_REFRESHING and no customer IDs or cursor.
Replay changes after the snapshot#
{
"success": true,
"data": {
"group_id": "58ee109f-64f7-4a51-8d48-b0ca62c4236a",
"current_revision": 44,
"history_floor_revision": 12,
"through_revision": 44,
"group_deleted": false,
"changes": [
{"revision": 43, "operation": "added", "customer_id": "28f97875-549a-4ec3-a074-51b159411f99"},
{"revision": 44, "operation": "removed", "customer_id": "87964951-2890-4b72-bd8f-78625a76c737"}
],
"revisions": [
{"revision": 43, "change_kind": "members_changed", "added_count": 1, "removed_count": 0, "recorded_at": "2026-09-12T10:05:00Z"},
{"revision": 44, "change_kind": "members_changed", "added_count": 0, "removed_count": 1, "recorded_at": "2026-09-12T10:06:00Z"}
],
"has_more": false
}
}
Apply changes in the returned order: revision ascending, then operation in the fixed order added, removed, group_deleted, then customer UUID. A page contains at most limit change items, including a group_deleted marker. revisions supplies counts and timestamps for exactly the revisions touched by that page; counts can cover items split across more than one page. Persist each operation idempotently. For a page with has_more, checkpoint next_cursor only after the complete page is durable. For the final page, checkpoint through_revision only after the complete traversal is durable. Pagination is pinned to through_revision, so changes committed later do not enter the traversal. Begin the next traversal with after_revision equal to that checkpointed through_revision.
A removed operation means that the customer left this group. It does not mean that the customer withdrew marketing consent, and an added operation does not grant consent. Read and process marketing consent through the customer contract separately.
Promotional campaign control groups are configured by staff in Store Admin or the mobile app and are not Store API resources. An integration can synchronize a Customer Group with these endpoints and run its own authorized experiment, but it must enforce destination consent, suppression, assignment and deletion rules itself. The membership feed cannot launch a Tringify campaign or place a customer into a Tringify campaign's message or control arm.
Cursors, retention, and deletion#
Cursors are opaque, bound to the credential's store, group, endpoint, page limit, and captured revision, and expire no later than 24 hours after the first page was issued. Continuation requests accept only cursor; changing limit or after_revision requires a new first page. Store cursors exactly as returned and never inspect or modify them.
Change history is retained for at least 30 days. history_floor_revision is the highest revision already removed; complete replay is available only when after_revision is at or above that floor. CUSTOMER_GROUP_MEMBERSHIP_CURSOR_EXPIRED means the cursor's 24-hour lifetime ended or required change history was removed; take a new snapshot instead of treating the result as no changes. The change feed remains readable after group deletion and ends with a group_deleted operation without a customer_id. After that marker is durable, discard the obsolete local membership mirror. Snapshot requests for a deleted group return CUSTOMER_GROUP_MEMBERSHIP_DELETED.
HTTP |
Error code |
Recovery |
|---|---|---|
400 |
|
Start a new request with valid first-page parameters; do not reuse the cursor for another store, group, or endpoint. |
404 |
|
Verify the group belongs to the credential's store. |
409 |
|
Retry the first snapshot after automatic evaluation completes. |
409 |
|
Discard all partial snapshot pages and begin the snapshot again. |
410 |
|
Take a new snapshot, then resume replay from that snapshot revision. |
410 |
|
Stop snapshot synchronization; use the change feed to observe deletion when retained history permits. |
Receive membership webhooks#
A merchant can connect a saved Customer Group to a webhook endpoint. The endpoint receives customer_group_membership.snapshot while a complete membership is synchronized, then customer_group_membership.changes for later revisions. These deliveries use the standard webhook JSON envelope and request signature described in Webhook events. Verify the signature against the raw request body, deduplicate by envelope event_id, and return a successful response only after durably accepting the batch.
Process batches by subscription_id, generation_number, and batch_sequence. generation is the opaque UUID for one synchronization epoch. generation_number increases when a fresh synchronization starts, and batch_sequence increases within that generation. HTTP requests can complete out of order. Stage a later sequence until earlier sequences in that generation are durable. When a larger generation number appears, discard partial work from older generations and build the replacement membership from the new snapshot.
{
"subscription_id": "6cb6ffb3-2d41-49cb-bac6-e3229c499cd3",
"group_id": "bd74fa4d-13c3-4ae8-90ef-c4bed6bc6518",
"generation": "c010b2a7-dd8b-4b24-8a03-9201becafbdd",
"generation_number": 2,
"batch_sequence": 1,
"final": true,
"revision": 18,
"customer_ids": [
"014f36c2-2469-4cc5-a105-e785d7605416",
"bf655bea-c683-43e2-8792-16e4411d593d"
]
}
Collect every snapshot batch for one generation and promote it as the current membership only after all sequences through the batch marked final: true are durable. An empty final snapshot is a valid empty group. final closes that snapshot or change-page sequence; it does not close the connection.
{
"subscription_id": "6cb6ffb3-2d41-49cb-bac6-e3229c499cd3",
"group_id": "bd74fa4d-13c3-4ae8-90ef-c4bed6bc6518",
"generation": "c010b2a7-dd8b-4b24-8a03-9201becafbdd",
"generation_number": 2,
"batch_sequence": 2,
"final": true,
"from_revision": 18,
"through_revision": 20,
"changes": [
{"revision": 19, "operation": "added", "customer_id": "8a4d941f-5541-4f54-9a71-29c4dc063987"},
{"revision": 20, "operation": "removed", "customer_id": "014f36c2-2469-4cc5-a105-e785d7605416"}
],
"revisions": [
{"revision": 19, "change_kind": "members_changed", "added_count": 1, "removed_count": 0, "recorded_at": "2026-09-12T09:30:00Z"},
{"revision": 20, "change_kind": "members_changed", "added_count": 0, "removed_count": 1, "recorded_at": "2026-09-12T09:31:00Z"}
],
"group_deleted": false
}
Apply changes in their supplied order and make each revision idempotent. added inserts a customer UUID and removed removes it. revisions describes the source revisions represented by the batch and can include a revision with no item changes. Use through_revision as the durable source checkpoint only after the complete batch is accepted. A checkpoint shown by Tringify means the batch was queued; it does not prove that your endpoint received it.
When group_deleted is true, the changes include a terminal group_deleted operation without customer_id. Apply all earlier operations in the batch before removing the local group. Delivery is at least once, so retries can repeat the same event_id. Pausing and reconnecting begins a new generation with sequence 1 and a fresh snapshot. Removing the connection deletes its queued and historical deliveries, although a request already in flight can complete.
Endpoints#
Customer Group limits
Get Customer Group limits.
Returns field, list, rule, membership, and store limits enforced by Customer Group endpoints.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
List Customer Groups
List Customer Groups for the store.
Returns a paginated, searchable, sortable list of manual and automatic groups.
Query parameters
| Name | Type | Description |
|---|---|---|
page
|
integer |
Page number. |
limit
|
integer |
Page size. |
search
|
string |
Search by name or slug. |
group_type
|
string |
Filter by |
sort_by
|
string |
Sort by name, member_count, created_at, or updated_at.
Allowed values
namemember_countcreated_atupdated_at |
sort_direction
|
string |
Sort direction:
Allowed values
ascdesc |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Get a Customer Group
Get one Customer Group.
Returns the group, its rules, and membership freshness when it belongs to the token's store. membership.ready is false while membership needs refreshing. within_last uses rolling 24-hour days; date boundaries and birthday rules use UTC. birthday_within includes today through the selected number of days ahead; February 29 maps to February 28 in non-leap years.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Create a Customer Group
Create a Customer Group.
Creates a manual group or an automatic group whose members are selected by its rules.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
name
required
|
string |
Group name. |
slug
|
string |
Group slug. Omit on create to generate it from the name. |
description
|
string |
Merchant description. |
type
|
string |
Group type: |
match_type
|
string |
Whether all or any rules must match. |
rules
|
array<object> |
Automatic rules shaped as |
Update a Customer Group
Update a Customer Group.
Updates supplied fields. Supplying rules replaces an automatic group's existing rules and refreshes its membership.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
name
|
string |
Group name. |
slug
|
string |
Group slug. Omit on create to generate it from the name. |
description
|
string |
Merchant description. |
type
|
string |
Group type: |
match_type
|
string |
Whether all or any rules must match. |
rules
|
array<object> |
Automatic rules shaped as |
Delete a Customer Group
Delete a Customer Group.
Deletes the group and its memberships without deleting customer profiles.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Preview automatic rules
Preview Customer Group rules.
Returns the matching customer count and a sample without saving a group.
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
match_type
required
|
string |
Whether all or any rules must match. |
rules
required
|
array<object> |
Rules to preview. |
List group members
List members of a Customer Group.
Returns store-scoped members with pagination, literal name/email search, and sorting. Each page and its membership metadata use one snapshot. Check membership.ready before reuse; evaluated_at and valid_until are ISO timestamps. A rolling-date selection expires after five minutes; birthday selections expire at UTC midnight. Dirty or never-evaluated automatic groups are not ready. Pages from separate requests are not a frozen export. Membership does not grant marketing consent.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Query parameters
| Name | Type | Description |
|---|---|---|
page
|
integer |
Page number. |
limit
|
integer |
Page size. |
search
|
string |
Search member names or email addresses. |
sort_by
|
string |
Member sort field.
Allowed values
namemember_countcreated_atupdated_at |
sort_direction
|
string |
Sort direction:
Allowed values
ascdesc |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
List candidate members
List customers that can be added to a manual group.
Returns active customers in the token's store that are not already members.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Query parameters
| Name | Type | Description |
|---|---|---|
page
|
integer |
Page number. |
limit
|
integer |
Page size. |
search
|
string |
Search member names or email addresses. |
sort_by
|
string |
Member sort field.
Allowed values
namemember_countcreated_atupdated_at |
sort_direction
|
string |
Sort direction:
Allowed values
ascdesc |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Add group members
Add customers to a manual group.
Adds valid store-scoped customers and returns the number newly added.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
customer_ids
required
|
array<string> |
Customer UUIDs to add. |
Remove group members
Remove customers from a manual group.
Removes matching memberships in the token's store.
Path parameters
| Name | Type | Description |
|---|---|---|
id
required
|
string |
Customer Group UUID. |
Headers
| Name | Type | Description |
|---|---|---|
Authorization
required
|
string |
Bearer Store API token. |
Body parameters
| Name | Type | Description |
|---|---|---|
customer_ids
required
|
array<string> |
Customer UUIDs to remove. |
Fields#
Field |
Type |
Create |
Update |
Required |
Translatable |
|---|---|---|---|---|---|
|
text |
Yes |
Yes |
create |
No |
|
slug |
Yes |
Yes |
No |
|
|
textarea |
Yes |
Yes |
No |
|
|
select |
Yes |
Yes |
No |
|
|
select |
Yes |
Yes |
No |
|
|
integer |
No |
No |
No |
|
|
datetime |
No |
No |
No |
|
|
datetime |
No |
No |
No |
|
|
datetime |
No |
No |
No |
Webhook events#
Subscribe to these events to keep your integration in sync with customer groups. Each event shows the required Store API scope and store permission, the available payload fields, and an example payload. Subscription setup is validated against those permissions; delivery is always restricted to the subscription's store.
Customer group created
Fired when a customer group is created.
Payload fields
| Field | Type | Description |
|---|---|---|
group.id |
string |
Always present |
group.name |
string |
Selectable in the Payload builder |
group.slug |
string |
Selectable in the Payload builder |
group.description |
string |
Selectable in the Payload builder |
group.type |
string |
Selectable in the Payload builder |
group.match_type |
string |
Selectable in the Payload builder |
group.member_count |
integer |
Selectable in the Payload builder |
group.created_at |
string |
Selectable in the Payload builder |
group.updated_at |
string |
Selectable in the Payload builder |
Customer group updated
Fired when a customer group's definition changes.
Possible changed_fields values
The changed_fields array names exactly which of these changed in this event — use it to skip processing updates your integration doesn't care about.
description match_type name rules slug
Payload fields
| Field | Type | Description |
|---|---|---|
previous.id |
string |
Always present |
previous.name |
string |
Selectable in the Payload builder |
previous.slug |
string |
Selectable in the Payload builder |
previous.description |
string |
Selectable in the Payload builder |
previous.type |
string |
Selectable in the Payload builder |
previous.match_type |
string |
Selectable in the Payload builder |
previous.member_count |
integer |
Selectable in the Payload builder |
previous.created_at |
string |
Selectable in the Payload builder |
previous.updated_at |
string |
Selectable in the Payload builder |
current.id |
string |
Always present |
current.name |
string |
Selectable in the Payload builder |
current.slug |
string |
Selectable in the Payload builder |
current.description |
string |
Selectable in the Payload builder |
current.type |
string |
Selectable in the Payload builder |
current.match_type |
string |
Selectable in the Payload builder |
current.member_count |
integer |
Selectable in the Payload builder |
current.created_at |
string |
Selectable in the Payload builder |
current.updated_at |
string |
Selectable in the Payload builder |
changed_fields |
array |
Selectable in the Payload builder |
Customer group deleted
Fired when a customer group is deleted.
Payload fields
| Field | Type | Description |
|---|---|---|
group.id |
string |
Always present |
group.name |
string |
Selectable in the Payload builder |
group.slug |
string |
Selectable in the Payload builder |
group.description |
string |
Selectable in the Payload builder |
group.type |
string |
Selectable in the Payload builder |
group.match_type |
string |
Selectable in the Payload builder |
group.member_count |
integer |
Selectable in the Payload builder |
group.created_at |
string |
Selectable in the Payload builder |
group.updated_at |
string |
Selectable in the Payload builder |