Documentation

Promotions and subscriptions

Newsletter signup, opt-in/opt-out, marketing preferences, and promotion-based subscription plans.

The promotions module covers customer-side subscriptions to marketing programmes — newsletters, drip campaigns, loyalty tiers, opt-in lists. It is distinct from commerce subscriptions (recurring product billing) and from subscription plans on the org-management side. Use this for "subscribe to our weekly digest" and "join our VIP rewards programme".

Subscribe and confirm

POST/crm/promotions/:name/subscribeNo auth
GET/crm/promotions/confirm/:tokenNo auth
POST/crm/promotions/:name/unsubscribeNo auth

Public so a marketing site or storefront footer can call them without auth. subscribe accepts an email and optional metadata; the platform sends a confirmation email with a tokenised link, and confirm/:token flips the subscription to confirmed. This is double-opt-in by default — required by GDPR and most major email vendors.

await fetch(`/api/crm/promotions/weekly-digest/subscribe`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', orgid: ORG_ID },
  body: JSON.stringify({
    email: '[email protected]',
    name: 'Alice',
    metadata: { source: 'footer-form' },
  }),
});

unsubscribe is by email — the unsubscribe link in every marketing email points at this endpoint with the email hashed into the URL.

Promotion CRUD

POST/crm/promotionsJWT
GET/crm/promotionsJWT
GET/crm/promotions/:nameJWT
PUT/crm/promotions/:nameJWT
DELETE/crm/promotions/:nameJWT

A promotion body:

{
  "name": "weekly-digest",
  "title": "Weekly Digest",
  "description": "Hand-picked stories every Monday.",
  "doubleOptIn": true,
  "incentive": {
    "type": "discount-code",
    "value": "WELCOME10",
    "amount": 10,
    "currency": "USD"
  },
  "tags": ["newsletter", "weekly"]
}

Setting an incentive means new subscribers get a reward on confirmation — a discount code, a downloadable asset, or credit to their wallet. The incentive-claimed endpoint records that the reward was issued so you don't double-grant.

Subscriber management

GET/crm/promotions/:name/subscribersJWT
POST/crm/promotions/:name/incentive-claimedJWT

Subscribers list with pagination, filtering by status (pending, confirmed, unsubscribed, bounced). Use this list as the audience for Marketing campaigns — the campaign builder accepts a promotion name as a shortcut for "all confirmed subscribers".

Customer preferences

GET/crm/promotions/preferencesJWT

Returns the current customer's full subscription state across every promotion in the org — which they're on, which they've left, which they're pending. A "manage preferences" page in your customer portal calls this.

Promotion as audience

A promotion is implicitly an audience: confirmed subscribers form a dynamic list. The Marketing module's audience picker shows promotions alongside saved segments. Cross-link them by passing audienceType: "promotion" and audienceId: <promotion-name> when creating a campaign.

Compliance

Every subscription stores consentedAt, consentSource, consentIp, and the unsubscribe history. When a customer requests a data export under GDPR, the platform pulls these fields automatically. Bounced and unsubscribed addresses are excluded from sends — the sync queue blocks them at dispatch time, not at audience-evaluation time, so you can safely target a stale list.

Disabling doubleOptIn is supported but not recommended. Major mailbox providers (Gmail, Outlook, Yahoo) will throttle or block senders whose lists show high single-opt-in volume.