CRM
Profiles, profile authentication, and audience subscriptions
The CRM module manages profile records and audience subscriptions for your store. Profiles are store-scoped — each store has its own profile list. A profile can be linked to orders and audience subscriptions, and can authenticate via email magic codes.
Key Concepts
Profile vs Account
- Account — platform-level auth identity. One person, one Account across all stores.
- Profile — store-level CRM record with one email, verification state, status, and taxonomies. Each store has its own view of a profile.
Taxonomies
Profile classification and searchable custom fields are stored as taxonomies — the same content model used across the platform. There is no direct profile tags field; tags and segments are taxonomy entries.
Profiles have a single email. The initialize flow creates an anonymous guest profile; the email is set once the profile authenticates with requestCode / verify, calls connect, or is updated by an admin.
Profile Endpoints
Create Profile (Admin)
/v1/stores/{storeId}/profiles sdk.crm.create() Creates a new profile. If a profile with the same email already exists, returns the existing one (idempotent).
Parameters
| Name | Type | Description |
|---|---|---|
email required | string | Primary email for the profile |
taxonomies optional | TaxonomyEntry[] | Taxonomy entries for segments, lifecycle stage, or other custom classification |
Initialize Profile (Public)
/v1/stores/{storeId}/profiles/initialize sdk.crm.initialize() Public endpoint for end users. Creates an anonymous guest profile and returns an auth token. No email required — use this to get a profile session before any identity is known. The returned token can later be upgraded by calling connect or requestCode + verify.
Connect Profile (Public)
/v1/stores/{storeId}/profiles/connect sdk.crm.connect() Attaches an email to the current profile session (or returns/creates a profile matching the email). Returns a fresh auth token.
Request Auth Code (Public)
/v1/stores/{storeId}/profiles/auth/code sdk.crm.requestCode() Sends a verification code to the given email address. Use this to start a profile login flow.
Verify Auth Code (Public)
/v1/stores/{storeId}/profiles/auth/verify sdk.crm.verify() Exchanges a verification code for an auth token, marking the profile as verified.
Refresh Token (Public)
/v1/stores/{storeId}/profiles/auth/refresh sdk.crm.refreshToken() Exchanges a refresh token for a new access token.
Get Current Profile
/v1/stores/{storeId}/profiles/me sdk.crm.getMe() Returns the currently authenticated profile based on the bearer token.
Get Profile
/v1/stores/{storeId}/profiles/{id} sdk.crm.get() Find Profiles
/v1/stores/{storeId}/profiles sdk.crm.find() Search and list profiles.
Parameters
| Name | Type | Description |
|---|---|---|
query optional | string | Search across profile email, ID, and indexed taxonomy values |
taxonomy_query optional | TaxonomyQuery[] | Filter by taxonomy entries |
status optional | 'active' | 'archived' | Filter by profile status |
limit optional | number | Items per page |
cursor optional | string | Pagination cursor |
sort_field optional | string | Field to sort by (e.g. createdAt) |
sort_direction optional | string | asc or desc |
Update Profile
/v1/stores/{storeId}/profiles/{id} sdk.crm.update() Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Profile ID |
email optional | string | Replace the profile's email |
taxonomies optional | TaxonomyEntry[] | Replace taxonomy entries |
status optional | 'active' | 'archived' | Profile status |
Merge Profiles
/v1/stores/{storeId}/profiles/{id}/merge sdk.crm.merge() Merges a source profile into the target. The source is permanently deleted (GDPR-compliant). Orders and audience subscriptions are re-pointed to the target profile.
Revoke Session Token
/v1/stores/{storeId}/profiles/{id}/sessions/{tokenId} sdk.crm.revokeToken() Revokes a single active session for a profile.
Revoke All Session Tokens
/v1/stores/{storeId}/profiles/{id}/sessions sdk.crm.revokeAllTokens() Revokes every active session for a profile (logout everywhere).
Profile Object
{
"id": "uuid",
"store_id": "uuid",
"email": "[email protected]",
"verified": true,
"status": "active",
"taxonomies": [],
"promo_usage": [],
"auth_tokens": [],
"verification_codes": [],
"created_at": 1234567890,
"updated_at": 1234567890
}
Audiences
Audiences are subscriber groups used for content gating, newsletters, and paid memberships. They live under the CRM module as sdk.crm.audiences.*.
Key Concepts
An audience is a subscriber group with two dimensions:
- Type:
standard(free) orpaid(Stripe checkout) - Confirmation: Optional double opt-in via an email template (
confirmTemplateId)
Use cases: newsletter subscriptions, premium memberships, course access, tiered content.
Create Audience
/v1/stores/{storeId}/audiences sdk.crm.audiences.create() Parameters
| Name | Type | Description |
|---|---|---|
key required | string | Unique audience identifier (alphanumeric, hyphens, underscores) |
type optional | AudienceType | standard (default) or paid with prices |
confirm_template_id optional | string | Email template ID for double opt-in. Omit for single opt-in. |
Get Audience
/v1/stores/{storeId}/audiences/{id} sdk.crm.audiences.get() Look up an audience by ID or by key.
Find Audiences
/v1/stores/{storeId}/audiences sdk.crm.audiences.find() Parameters
| Name | Type | Description |
|---|---|---|
ids optional | string[] | Filter by specific audience IDs |
status optional | string | Filter by status (active, archived) |
query optional | string | Search in audience keys |
limit optional | number | Items per page (default 50) |
cursor optional | string | Pagination cursor |
Update Audience
/v1/stores/{storeId}/audiences/{id} sdk.crm.audiences.update() The type cannot be changed after creation, but you can update the key, status, and confirmation template.
Subscribe to Audience
/v1/stores/{storeId}/audiences/{id}/subscribe sdk.crm.audiences.subscribe() Subscribe a profile to an audience. Behavior depends on the audience configuration:
- Standard, no confirmation: Subscription is immediately active
- Standard, with confirmation: Subscription is
pendinguntil the user clicks the confirmation link - Paid: Returns a Stripe checkout URL
Parameters
| Name | Type | Description |
|---|---|---|
id required | string | Audience ID |
profile_id required | string | Profile ID (obtain via sdk.crm.initialize or sdk.crm.connect) |
price_id optional | string | Stripe price ID (required for paid audiences) |
success_url optional | string | Redirect URL after successful payment |
cancel_url optional | string | Redirect URL if user cancels payment |
confirmUrl optional | string | Base URL for the confirmation link (required for audiences with confirmTemplateId) |
Check Access
/v1/stores/{storeId}/audiences/{id}/access sdk.crm.audiences.checkAccess() Check if the current profile has access to an audience.
Get Subscribers
/v1/stores/{storeId}/audiences/{id}/subscribers sdk.crm.audiences.getSubscribers() List subscribers for an audience (admin only).
Add Subscriber
/v1/stores/{storeId}/audiences/{id}/subscribers sdk.crm.audiences.addSubscriber() Add a subscriber by profile ID (admin only). Skips if already subscribed.
Remove Subscriber
/v1/stores/{storeId}/audiences/{id}/subscribers/{profileId} sdk.crm.audiences.removeSubscriber() Typical Flows
E-commerce Checkout
// 1. Initialize a guest profile session
const token = await sdk.crm.initialize();
// 2. Attach the shopper's email
await sdk.crm.connect({ email: shippingAddress.email });
// 3. Checkout a cart — the authenticated profile is resolved from the token
const cart = await sdk.eshop.cart.current();
await sdk.eshop.cart.update({
id: cart.id,
items: [...],
shipping_address: {...}
});
const order = await sdk.eshop.cart.checkout({ id: cart.id });
Newsletter Subscribe with Double Opt-in
// 1. Initialize + connect to get a profile
await sdk.crm.initialize();
const { id: profileId } = await sdk.crm.connect({ email: "[email protected]" });
// 2. Subscribe to the audience (sends confirmation email)
await sdk.crm.audiences.subscribe({
id: audienceId,
profileId,
confirmUrl: 'https://yoursite.com/confirm'
});
Profile Login (Magic Code)
// 1. Ask for a code
await sdk.crm.requestCode({ email: "[email protected]" });
// 2. User enters the code from their email
const token = await sdk.crm.verify({
email: "[email protected]",
code: "123456"
});
// 3. Persist token.refresh_token and use token.access_token for subsequent calls
If you delete a confirmation email template, any audience referencing it will automatically lose its double opt-in and fall back to single opt-in.