Back to arkyStore.io

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.

Email

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)

POST /v1/stores/{storeId}/profiles
SDK: 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)

POST /v1/stores/{storeId}/profiles/initialize
SDK: 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)

POST /v1/stores/{storeId}/profiles/connect
SDK: 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)

POST /v1/stores/{storeId}/profiles/auth/code
SDK: sdk.crm.requestCode()

Sends a verification code to the given email address. Use this to start a profile login flow.

Verify Auth Code (Public)

POST /v1/stores/{storeId}/profiles/auth/verify
SDK: sdk.crm.verify()

Exchanges a verification code for an auth token, marking the profile as verified.

Refresh Token (Public)

POST /v1/stores/{storeId}/profiles/auth/refresh
SDK: sdk.crm.refreshToken()

Exchanges a refresh token for a new access token.

Get Current Profile

GET /v1/stores/{storeId}/profiles/me
SDK: sdk.crm.getMe()

Returns the currently authenticated profile based on the bearer token.

Get Profile

GET /v1/stores/{storeId}/profiles/{id}
SDK: sdk.crm.get()

Find Profiles

GET /v1/stores/{storeId}/profiles
SDK: 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

PUT /v1/stores/{storeId}/profiles/{id}
SDK: 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

POST /v1/stores/{storeId}/profiles/{id}/merge
SDK: 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

DELETE /v1/stores/{storeId}/profiles/{id}/sessions/{tokenId}
SDK: sdk.crm.revokeToken()

Revokes a single active session for a profile.

Revoke All Session Tokens

DELETE /v1/stores/{storeId}/profiles/{id}/sessions
SDK: 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) or paid (Stripe checkout)
  • Confirmation: Optional double opt-in via an email template (confirmTemplateId)

Use cases: newsletter subscriptions, premium memberships, course access, tiered content.

Create Audience

POST /v1/stores/{storeId}/audiences
SDK: 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

GET /v1/stores/{storeId}/audiences/{id}
SDK: sdk.crm.audiences.get()

Look up an audience by ID or by key.

Find Audiences

GET /v1/stores/{storeId}/audiences
SDK: 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

PUT /v1/stores/{storeId}/audiences/{id}
SDK: sdk.crm.audiences.update()

The type cannot be changed after creation, but you can update the key, status, and confirmation template.

Subscribe to Audience

POST /v1/stores/{storeId}/audiences/{id}/subscribe
SDK: 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 pending until 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

GET /v1/stores/{storeId}/audiences/{id}/access
SDK: sdk.crm.audiences.checkAccess()

Check if the current profile has access to an audience.

Get Subscribers

GET /v1/stores/{storeId}/audiences/{id}/subscribers
SDK: sdk.crm.audiences.getSubscribers()

List subscribers for an audience (admin only).

Add Subscriber

POST /v1/stores/{storeId}/audiences/{id}/subscribers
SDK: sdk.crm.audiences.addSubscriber()

Add a subscriber by profile ID (admin only). Skips if already subscribed.

Remove Subscriber

DELETE /v1/stores/{storeId}/audiences/{id}/subscribers/{profileId}
SDK: 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
Tip

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.