@86d-app/customer-groups
v0.0.25
Published
Customer groups and segmentation module for 86d commerce platform
Maintainers
Readme
[!WARNING] This project is under active development and is not ready for production use. Please proceed with caution. Use at your own risk.
Customer Groups Module
Customer segmentation module for grouping customers into manual or rule-based segments. Enables wholesale pricing, VIP tiers, B2B customer management, and group-specific price adjustments.
Installation
npm install @86d-app/customer-groupsUsage
import customerGroups from "@86d-app/customer-groups";
const module = customerGroups({
defaultGroupSlug: "retail",
includeExpiredMemberships: false,
});Configuration
| Option | Type | Default | Description |
|---|---|---|---|
| defaultGroupSlug | string | undefined | Slug of group to auto-assign new customers to |
| includeExpiredMemberships | boolean | false | Whether to include expired memberships in group lookups |
Store Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /customer-groups/mine | Get all groups the current customer belongs to |
| GET | /customer-groups/pricing | Get active price adjustments for the current customer |
Admin Endpoints
Groups
| Method | Path | Description |
|---|---|---|
| GET | /admin/customer-groups | List all groups (filterable by type, active status) |
| POST | /admin/customer-groups/create | Create a new group |
| GET | /admin/customer-groups/stats | Get group statistics |
| GET | /admin/customer-groups/:id | Get group details |
| POST | /admin/customer-groups/:id/update | Update a group |
| POST | /admin/customer-groups/:id/delete | Delete a group (cascades) |
Members
| Method | Path | Description |
|---|---|---|
| GET | /admin/customer-groups/:id/members | List group members |
| POST | /admin/customer-groups/:id/members/add | Add a customer to a group |
| POST | /admin/customer-groups/:id/members/remove | Remove a customer from a group |
Rules (Automatic Groups)
| Method | Path | Description |
|---|---|---|
| POST | /admin/customer-groups/:id/rules/add | Add a segmentation rule |
| POST | /admin/customer-groups/rules/:ruleId/remove | Remove a rule |
| POST | /admin/customer-groups/evaluate | Evaluate rules against customer data |
Pricing
| Method | Path | Description |
|---|---|---|
| POST | /admin/customer-groups/:id/pricing | Set a price adjustment for a group |
| GET | /admin/customer-groups/:id/pricing/list | List price adjustments |
| POST | /admin/customer-groups/pricing/:adjustmentId/remove | Remove a price adjustment |
Controller API
interface CustomerGroupController {
createGroup(params: { name: string; slug: string; description?: string; type?: GroupType; priority?: number }): Promise<CustomerGroup>;
getGroup(id: string): Promise<CustomerGroup | null>;
getGroupBySlug(slug: string): Promise<CustomerGroup | null>;
listGroups(opts?: { type?: GroupType; activeOnly?: boolean }): Promise<CustomerGroup[]>;
updateGroup(id: string, data: Partial<CustomerGroup>): Promise<CustomerGroup>;
deleteGroup(id: string): Promise<void>;
addMember(params: { groupId: string; customerId: string; expiresAt?: Date }): Promise<GroupMembership>;
removeMember(groupId: string, customerId: string): Promise<void>;
listMembers(groupId: string, opts?: { includeExpired?: boolean }): Promise<GroupMembership[]>;
getCustomerGroups(customerId: string, opts?: { activeOnly?: boolean }): Promise<CustomerGroup[]>;
isMember(groupId: string, customerId: string): Promise<boolean>;
addRule(params: { groupId: string; field: string; operator: RuleOperator; value: string }): Promise<GroupRule>;
removeRule(ruleId: string): Promise<void>;
listRules(groupId: string): Promise<GroupRule[]>;
evaluateRules(customerData: Record<string, unknown>): Promise<string[]>;
setPriceAdjustment(params: { groupId: string; adjustmentType: AdjustmentType; value: number; scope?: AdjustmentScope; scopeId?: string }): Promise<GroupPriceAdjustment>;
removePriceAdjustment(id: string): Promise<void>;
listPriceAdjustments(groupId: string): Promise<GroupPriceAdjustment[]>;
getCustomerPricing(customerId: string, opts?: { scope?: AdjustmentScope; scopeId?: string }): Promise<GroupPriceAdjustment[]>;
getStats(): Promise<GroupStats>;
}Types
type GroupType = "manual" | "automatic";
type RuleOperator = "equals" | "not_equals" | "contains" | "not_contains" | "greater_than" | "less_than" | "in" | "not_in";
type AdjustmentType = "percentage" | "fixed";
type AdjustmentScope = "all" | "category" | "product";
interface CustomerGroup {
id: string;
name: string;
slug: string;
description?: string;
type: GroupType;
isActive: boolean;
priority: number;
metadata?: Record<string, unknown>;
createdAt: Date;
updatedAt: Date;
}
interface GroupMembership {
id: string;
groupId: string;
customerId: string;
joinedAt: Date;
expiresAt?: Date;
metadata?: Record<string, unknown>;
}
interface GroupRule {
id: string;
groupId: string;
field: string;
operator: RuleOperator;
value: string;
createdAt: Date;
}
interface GroupPriceAdjustment {
id: string;
groupId: string;
adjustmentType: AdjustmentType;
value: number;
scope: AdjustmentScope;
scopeId?: string;
createdAt: Date;
updatedAt: Date;
}Notes
- Two group types:
manualgroups require explicit membership management.automaticgroups match customers via configurable rules. - Rule evaluation: All rules use AND logic — every rule must match for a customer to belong to an automatic group. Rules with no conditions never match.
- Price adjustments: Each group can have multiple price adjustments scoped to
all,category, orproduct. Setting an adjustment with the same scope/scopeId updates the existing one. - Membership expiration: Memberships can have an
expiresAtdate. Expired memberships are excluded from all lookups by default. - Cascade delete: Deleting a group removes all associated memberships, rules, and price adjustments.
- Priority ordering: Groups are sorted by
priority(ascending) — lower numbers appear first.
