Audiences

Content gating, memberships, and subscription management

Audiences enable content gating and membership management. Create free or paid audiences that gate access to specific CMS content nodes, with built-in Stripe integration for subscriptions.

Key Concepts

What is an Audience?

An audience is a group that controls access to content:

  • Free audiences: Users subscribe instantly, no payment required
  • Paid audiences: Users pay via Stripe checkout (one-time or recurring)
  • Content gating: Link audiences to CMS nodes to restrict access
┌─────────────────┐         ┌─────────────────┐
│    Audience     │────────▶│   CMS Nodes     │
│  "Premium"      │         │ (gated content) │
│                 │         └─────────────────┘
│  prices: [...]  │
│  nodeIds: [...]│
└─────────────────┘


┌─────────────────┐
│   Subscribers   │
│  (users with    │
│    access)      │
└─────────────────┘

Use Cases

  • Newsletter subscriptions (free)
  • Premium content memberships (paid, recurring)
  • Course access (paid, one-time)
  • Exclusive community access
  • Tiered content (multiple audiences)

Create Audience

POST /v1/businesses/{businessId}/audiences
SDK: sdk.audience.createAudience()

Create a new audience.

// Free newsletter audience
const freeAudience = await sdk.audience.createAudience({
key: 'newsletter',
name: 'Newsletter Subscribers',
nodeIds: ['node_exclusive_content_1', 'node_exclusive_content_2']
});

// Paid membership audience
const paidAudience = await sdk.audience.createAudience({
key: 'premium-members',
name: 'Premium Members',
nodeIds: ['node_premium_articles', 'node_premium_videos'],
prices: [
  {
    market: 'USD',
    amount: 999, // $9.99/month
    type: 'RECURRING',
    providerPriceId: 'price_stripe_xxx' // From Stripe
  },
  {
    market: 'EUR',
    amount: 899, // €8.99/month
    type: 'RECURRING',
    providerPriceId: 'price_stripe_yyy'
  }
]
});

Parameters

Name Type Description
key required string Unique audience identifier (alphanumeric, hyphens, underscores)
name required string Display name for the audience
nodeIds optional string[] CMS node IDs this audience grants access to
prices optional AudiencePrice[] Pricing configuration (omit for free audiences)

AudiencePrice Object

Parameters

Name Type Description
market required string Currency code (USD, EUR, etc.)
amount required number Price in minor units (cents). Use 0 for free.
type required RECURRING | ONE_TIME Subscription type
providerPriceId optional string Stripe price ID (required for paid audiences)
compareAt optional number Original price for displaying discounts
freeThreshold optional number Free trial threshold

Get Audience

GET /v1/businesses/{businessId}/audiences/{id}
SDK: sdk.audience.getAudience()

Retrieve an audience by ID.

const result = await sdk.audience.getAudience({
  id: 'aud_abc123'
});

if (result.ok) {
  const audience = result.val;
  console.log(audience.key, audience.name);
  console.log('Gated nodes:', audience.nodeIds);
  console.log('Prices:', audience.prices);
}

List Audiences

GET /v1/businesses/{businessId}/audiences
SDK: sdk.audience.getAudiences()

List audiences with filtering.

const result = await sdk.audience.getAudiences({
  statuses: ['ACTIVE'],
  query: 'premium',
  limit: 20
});

if (result.ok) {
  const { items, cursor } = result.val;
  items.forEach(audience => {
    console.log(audience.key, audience.name);
  });
}

Parameters

Name Type Description
ids optional string[] Filter by specific audience IDs
nodeId optional string Filter by audiences that gate a specific node
statuses optional string[] Filter by status (ACTIVE, DRAFT, ARCHIVED)
query optional string Search in audience keys
limit optional number Items per page (max 100)
cursor optional string Pagination cursor

Update Audience

PUT /v1/businesses/{businessId}/audiences/{id}
SDK: sdk.audience.updateAudience()

Update an existing audience.

const result = await sdk.audience.updateAudience({
  id: 'aud_abc123',
  name: 'Premium Plus Members',
  nodeIds: ['node_1', 'node_2', 'node_3'], // Add more gated content
  status: 'ACTIVE'
});

Parameters

Name Type Description
id required string Audience ID to update
key optional string New unique key
name optional string New display name
nodeIds optional string[] Updated gated node IDs
prices optional AudiencePrice[] Updated pricing
status optional ACTIVE | DRAFT | ARCHIVED Audience status

Delete Audience

DELETE /v1/businesses/{businessId}/audiences/{id}
SDK: sdk.audience.deleteAudience()

Archive an audience. This revokes access for all subscribers.

const result = await sdk.audience.deleteAudience({
  id: 'aud_abc123'
});
Warning

Deleting an audience will revoke access for all current subscribers. This action cannot be undone.

Subscribe to Audience

POST /v1/businesses/{businessId}/audiences/{id}/subscribe
SDK: sdk.audience.subscribe()

Subscribe the current user to an audience. For paid audiences, returns a Stripe checkout URL.

// Free audience - instant subscription
const freeResult = await sdk.audience.subscribe({
id: 'aud_newsletter',
successUrl: 'https://yoursite.com/welcome',
cancelUrl: 'https://yoursite.com/subscribe'
});

// Paid audience - returns Stripe checkout URL
const paidResult = await sdk.audience.subscribe({
id: 'aud_premium',
priceId: 'price_stripe_xxx', // Required for paid audiences
successUrl: 'https://yoursite.com/welcome',
cancelUrl: 'https://yoursite.com/pricing'
});

if (paidResult.ok && paidResult.val.checkoutUrl) {
// Redirect to Stripe checkout
window.location.href = paidResult.val.checkoutUrl;
}

Parameters

Name Type Description
id required string Audience ID to subscribe to
priceId optional string Stripe price ID (required for paid audiences)
successUrl required string Redirect URL after successful subscription
cancelUrl required string Redirect URL if user cancels

Response

Free audience:

{
  "subscription": {
    "target": "audience:aud_abc123",
    "status": "ACTIVE",
    "startDate": 1704067200
  }
}

Paid audience:

{
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_xxx"
}

Check Access

GET /v1/businesses/{businessId}/audiences/{id}/access
SDK: sdk.audience.checkAccess()

Check if the current user has access to an audience.

const result = await sdk.audience.checkAccess({
  id: 'aud_premium'
});

if (result.ok) {
  const { hasAccess, subscription } = result.val;

  if (hasAccess) {
    console.log('User has access!');
    console.log('Subscription status:', subscription.status);
  } else {
    console.log('User needs to subscribe');
  }
}

Response

{
  "hasAccess": true,
  "subscription": {
    "target": "audience:aud_abc123",
    "status": "ACTIVE",
    "startDate": 1704067200,
    "endDate": null
  }
}

Get Subscribers

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

List subscribers for an audience (admin only).

const result = await sdk.audience.getSubscribers({
  id: 'aud_premium',
  limit: 50,
  cursor: null
});

if (result.ok) {
  const { items, cursor } = result.val;
  items.forEach(subscriber => {
    console.log(subscriber.accountId, subscriber.email);
  });
}

Parameters

Name Type Description
id required string Audience ID
limit optional number Items per page (max 100)
cursor optional string Pagination cursor

Revoke Subscription

DELETE /v1/businesses/{businessId}/audiences/{id}/subscribers/{accountId}
SDK: sdk.audience.revokeSubscription()

Remove a user’s subscription to an audience (admin only).

const result = await sdk.audience.revokeSubscription({
  id: 'aud_premium',
  accountId: 'acc_user123'
});

Parameters

Name Type Description
id required string Audience ID
accountId required string User account ID to revoke

Complete Example: Membership Site

// 1. Create your audiences
const freeAudience = await sdk.audience.createAudience({
  key: 'free-tier',
  name: 'Free Members',
  nodeIds: ['node_free_articles']
});

const premiumAudience = await sdk.audience.createAudience({
  key: 'premium-tier',
  name: 'Premium Members',
  nodeIds: ['node_free_articles', 'node_premium_articles', 'node_premium_videos'],
  prices: [{
    market: 'USD',
    amount: 1999, // $19.99/month
    type: 'RECURRING',
    providerPriceId: 'price_xxx'
  }]
});

// 2. On your content page, check access
async function loadContent(nodeId: string) {
  // Check if user can access premium content
  const accessResult = await sdk.audience.checkAccess({
    id: premiumAudience.val.id
  });

  if (accessResult.ok && accessResult.val.hasAccess) {
    // Load full content
    const node = await sdk.cms.getNode({ id: nodeId });
    return node.val;
  } else {
    // Show teaser + upgrade CTA
    return { showPaywall: true };
  }
}

// 3. Handle subscription
async function handleSubscribe(audienceId: string, priceId?: string) {
  const result = await sdk.audience.subscribe({
    id: audienceId,
    priceId,
    successUrl: `${window.location.origin}/welcome`,
    cancelUrl: `${window.location.origin}/pricing`
  });

  if (result.ok) {
    if (result.val.checkoutUrl) {
      // Paid - redirect to Stripe
      window.location.href = result.val.checkoutUrl;
    } else {
      // Free - already subscribed
      window.location.href = '/welcome';
    }
  }
}

Webhooks for Subscription Events

Handle subscription lifecycle events via webhooks:

// In your webhook handler
app.post('/webhooks/arky', async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'audience.subscription.created':
      // New subscriber
      console.log('New subscriber:', event.data.accountId);
      // Send welcome email, provision access, etc.
      break;

    case 'audience.subscription.cancelled':
      // Subscription cancelled
      console.log('Subscription cancelled:', event.data.accountId);
      // Revoke access, send win-back email, etc.
      break;
  }

  res.json({ received: true });
});
Tip

For paid audiences, Arky automatically handles Stripe webhooks for subscription lifecycle events. The subscription status updates automatically when payments succeed or fail.