Back to arky.io

CRM

Customers, customer authentication, and audience subscriptions

The CRM module manages customer records and audience subscriptions for your business. Customers are business-scoped — each business has its own customer list. A customer can be linked to orders, bookings, reactions, and audience subscriptions, and can authenticate via email magic codes.

Key Concepts

Customer vs Account

  • Account — platform-level auth identity. One person, one Account across all businesses.
  • Customer — business-level CRM record (emails, blocks, taxonomies, audience subscriptions). Each business has its own view of a customer.

Blocks and Taxonomies

Customer data (name, phone, addresses, notes, tags, etc.) is stored as blocks and taxonomies — the same content model used by the CMS. This lets a business define its own custom customer fields instead of being locked into a fixed schema.

Multiple Emails

Customers support multiple emails. The initialize flow creates an anonymous guest customer; emails are attached once the customer authenticates with requestCode / verify or is updated by an admin.

Customer Endpoints

Create Customer (Admin)

POST /v1/businesses/{businessId}/customers
SDK: sdk.crm.create()

Creates a new customer. If a customer with the same email already exists, returns the existing one (idempotent).

Parameters

Name Type Description
email required string Primary email for the customer
blocks optional Block[] Structured content blocks (name, phone, addresses, notes, etc.)
taxonomies optional TaxonomyEntry[] Taxonomy entries — tags, segments, lifecycle stage

Initialize Customer (Public)

POST /v1/businesses/{businessId}/customers/initialize
SDK: sdk.crm.initialize()

Public endpoint for end users. Creates an anonymous guest customer and returns an auth token. No email required — use this to get a customer session before any identity is known. The returned token can later be upgraded by calling connect or requestCode + verify.

Connect Customer (Public)

POST /v1/businesses/{businessId}/customers/connect
SDK: sdk.crm.connect()

Attaches an email to the current customer session (or returns/creates a customer matching the email). Returns a fresh auth token.

Request Auth Code (Public)

POST /v1/businesses/{businessId}/customers/auth/code
SDK: sdk.crm.requestCode()

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

Verify Auth Code (Public)

POST /v1/businesses/{businessId}/customers/auth/verify
SDK: sdk.crm.verify()

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

Refresh Token (Public)

POST /v1/businesses/{businessId}/customers/auth/refresh
SDK: sdk.crm.refreshToken()

Exchanges a refresh token for a new access token.

Get Current Customer

GET /v1/businesses/{businessId}/customers/me
SDK: sdk.crm.getMe()

Returns the currently authenticated customer based on the bearer token.

Get Customer

GET /v1/businesses/{businessId}/customers/{id}
SDK: sdk.crm.get()

Find Customers

GET /v1/businesses/{businessId}/customers
SDK: sdk.crm.find()

Search and list customers.

Parameters

Name Type Description
query optional string Search across customer emails and blocks
limit optional number Items per page
cursor optional string Pagination cursor
sortField optional string Field to sort by (e.g. createdAt)
sortDirection optional string asc or desc

Update Customer

PUT /v1/businesses/{businessId}/customers/{id}
SDK: sdk.crm.update()

Parameters

Name Type Description
id required string Customer ID
emails optional string[] Replace the customer's email list
blocks optional Block[] Replace customer blocks
taxonomies optional TaxonomyEntry[] Replace taxonomy entries
status optional 'active' | 'archived' Customer status

Merge Customers

POST /v1/businesses/{businessId}/customers/{id}/merge
SDK: sdk.crm.merge()

Merges a source customer into the target. Source emails, blocks, and subscriptions are appended to the target. The source is permanently deleted (GDPR-compliant). All orders, bookings, reactions, and audience subscriptions are re-pointed to the target customer.

Revoke Session Token

DELETE /v1/businesses/{businessId}/customers/{id}/sessions/{tokenId}
SDK: sdk.crm.revokeToken()

Revokes a single active session for a customer.

Revoke All Session Tokens

DELETE /v1/businesses/{businessId}/customers/{id}/sessions
SDK: sdk.crm.revokeAllTokens()

Revokes every active session for a customer (logout everywhere).

Customer Object

{
  "id": "uuid",
  "businessId": "uuid",
  "emails": ["john@example.com"],
  "status": "active",
  "blocks": [],
  "taxonomies": [],
  "promoUsage": [],
  "authTokens": [],
  "verificationCodes": [],
  "audienceSubscriptions": [],
  "reactions": [],
  "createdAt": 1234567890,
  "updatedAt": 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/businesses/{businessId}/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
confirmTemplateId optional string Email template ID for double opt-in. Omit for single opt-in.

Get Audience

GET /v1/businesses/{businessId}/audiences/{id}
SDK: sdk.crm.audiences.get()

Look up an audience by ID or by key.

Find Audiences

GET /v1/businesses/{businessId}/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/businesses/{businessId}/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/businesses/{businessId}/audiences/{id}/subscribe
SDK: sdk.crm.audiences.subscribe()

Subscribe a customer 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
customerId required string Customer ID (obtain via sdk.crm.initialize or sdk.crm.connect)
priceId optional string Stripe price ID (required for paid audiences)
successUrl optional string Redirect URL after successful payment
cancelUrl 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/businesses/{businessId}/audiences/{id}/access
SDK: sdk.crm.audiences.checkAccess()

Check if the current customer has access to an audience.

Get Subscribers

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

List subscribers for an audience (admin only).

Add Subscriber

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

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

Remove Subscriber

DELETE /v1/businesses/{businessId}/audiences/{id}/subscribers/{customerId}
SDK: sdk.crm.audiences.removeSubscriber()

Typical Flows

E-commerce Checkout

// 1. Initialize a guest customer session
const token = await sdk.crm.initialize();

// 2. Attach the shopper's email
await sdk.crm.connect({ email: shippingAddress.email });

// 3. Checkout — the authenticated customer is resolved from the token
const order = await sdk.eshop.checkout({
  items: [...],
  shippingAddress: {...}
});

Newsletter Subscribe with Double Opt-in

// 1. Initialize + connect to get a customer
await sdk.crm.initialize();
const { id: customerId } = await sdk.crm.connect({ email: "user@example.com" });

// 2. Subscribe to the audience (sends confirmation email)
await sdk.crm.audiences.subscribe({
  id: audienceId,
  customerId,
  confirmUrl: 'https://yoursite.com/confirm'
});

Customer Login (Magic Code)

// 1. Ask for a code
await sdk.crm.requestCode({ email: "user@example.com" });

// 2. User enters the code from their email
const token = await sdk.crm.verify({
  email: "user@example.com",
  code: "123456"
});

// 3. Persist token.refreshToken and use token.accessToken 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.