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
/v1/businesses/{businessId}/audiences 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
/v1/businesses/{businessId}/audiences/{id} 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
/v1/businesses/{businessId}/audiences 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
/v1/businesses/{businessId}/audiences/{id} 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
/v1/businesses/{businessId}/audiences/{id} sdk.audience.deleteAudience() Archive an audience. This revokes access for all subscribers.
const result = await sdk.audience.deleteAudience({
id: 'aud_abc123'
});
Deleting an audience will revoke access for all current subscribers. This action cannot be undone.
Subscribe to Audience
/v1/businesses/{businessId}/audiences/{id}/subscribe 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
/v1/businesses/{businessId}/audiences/{id}/access 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
/v1/businesses/{businessId}/audiences/{id}/subscribers 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
/v1/businesses/{businessId}/audiences/{id}/subscribers/{accountId} 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 });
});
For paid audiences, Arky automatically handles Stripe webhooks for subscription lifecycle events. The subscription status updates automatically when payments succeed or fail.