Skip to main content

Configuration Reference

Full reference for the SDKConfig object passed to ContentCredits.init().

interface SDKConfig {
// ── Required ──────────────────────────────────────────────────────────────
apiKey: string;

// ── Core options ──────────────────────────────────────────────────────────
articleUrl?: string;
contentSelector?: string;
teaserParagraphs?: number;
enableComments?: boolean;
extensionId?: string;
theme?: SDKTheme;
debug?: boolean;

// ── Paywall appearance ────────────────────────────────────────────────────
paywallMode?: 'overlay' | 'inline';
paywallTopSlot?: PaywallSlotItem[] | HTMLElement | ((container: HTMLElement) => void) | ReactElement;
reactDOM?: ReactDOMAdapter;

// ── Headless mode ─────────────────────────────────────────────────────────
headless?: boolean;

// ── Callbacks ─────────────────────────────────────────────────────────────
onAccessGranted?: () => void;
onStateChange?: (state: SDKState) => void;
onReady?: (state: SDKState) => void;
onLoginRequired?: () => void;
onPurchaseRequired?: (info: { requiredCredits: number | null; creditBalance: number | null }) => void;
onInsufficientCredits?: (info: { required: number; available: number }) => void;
onPurchased?: (info: { creditsSpent: number; remainingBalance: number }) => void;
onUserLogin?: (user: User) => void;
onUserLogout?: () => void;
onError?: (info: { message: string; error?: unknown }) => void;
}

Required

apiKey · string

Your publisher API key. Get it from accounts.contentcredits.com → Settings → API Keys.

apiKey: 'pub_abc123def456ghi789'

Must be non-empty. Throws Error if missing or blank.


Core options

articleUrl · string

Default: window.location.href

The canonical URL used to identify the article in the Content Credits system. Override this if your canonical URL differs from the browser's current URL.

articleUrl: 'https://yoursite.com/articles/my-article-slug'

contentSelector · string

Default: '.cc-premium-content'

CSS selector for the element(s) to gate. All matching elements are hidden until the reader pays.

contentSelector: '#premium-content'
contentSelector: '.paywalled'
contentSelector: 'article > section:last-of-type'

Not used in headless: true mode — you control content visibility yourself.


teaserParagraphs · number

Default: 2

Number of <p> elements inside the gated element to remain visible as a preview.

teaserParagraphs: 0   // hide everything immediately
teaserParagraphs: 3 // show 3 paragraphs before the overlay

Not used in headless: true mode.


enableComments · boolean

Default: true

Whether to show the floating comment widget and panel. Set to false to disable the comment system entirely.

enableComments: false

extensionId · string

Default: built-in Content Credits extension ID

Override the Chrome extension ID the SDK looks for. Only needed if you're using a custom extension build.

extensionId: 'abcdefghijklmnopqrstuvwxyz123456'

theme · SDKTheme

Visual customisation options for the built-in paywall overlay and comment panel. See Theming for full details.

interface SDKTheme {
primaryColor?: string; // default: '#44C678'
fontFamily?: string; // default: system UI
}
theme: {
primaryColor: '#0066cc',
fontFamily: "'Inter', system-ui, sans-serif",
}

Not used in headless: true mode.


Paywall appearance

paywallMode · 'overlay' | 'inline'

Default: 'overlay'

Controls how the paywall is presented to the reader.

ModeBehaviour
'overlay'Full-width panel that appears below the teaser with a gradient fade. The top section is customisable via paywallTopSlot. (Default)
'inline'Compact panel inserted directly below the teaser in the page flow.
paywallMode: 'overlay'  // default — full-width with customisable top slot
paywallMode: 'inline' // compact panel in page flow

paywallTopSlot

Default: undefined

Injects custom content into the top section of the overlay paywall (above the SDK's unlock controls). Ignored in inline mode and headless mode.

Accepts four formats:

1. Structured items array

paywallTopSlot: [
{ type: 'heading', content: 'Support independent journalism' },
{ type: 'text', content: 'Your credits go directly to the author.' },
{ type: 'divider', content: 'or' },
]

Supported item types: heading, subheading, text, button, divider.

2. Plain HTMLElement

const widget = document.getElementById('my-donation-widget');
paywallTopSlot: widget

3. Factory function

paywallTopSlot: (container) => {
container.innerHTML = '<p>Custom content rendered here</p>';
}

4. React element (JSX) — requires reactDOM option

import ReactDOM from 'react-dom/client';
import { DonationWidget } from './DonationWidget';

ContentCredits.init({
apiKey,
reactDOM: ReactDOM,
paywallTopSlot: <DonationWidget />,
});

See the Paywall customisation guide for full examples.


reactDOM · ReactDOMAdapter

Default: undefined

Pass your app's ReactDOM instance so the SDK can mount React elements provided via paywallTopSlot. Supports React 18 (createRoot) and React 16/17 (render).

import ReactDOM from 'react-dom/client';

ContentCredits.init({
apiKey,
reactDOM: ReactDOM,
paywallTopSlot: <MyWidget />,
});

Only required when paywallTopSlot is a React element. Has no effect otherwise.


debug · boolean

Default: false

Enables verbose console logging for development and troubleshooting.

debug: true

Headless mode

headless · boolean

Default: false

When true, the SDK does not touch the DOM at all — no content hiding, no gradient fade, no paywall overlay. You are responsible for all UI using the callbacks and action methods below.

headless: true

See the Headless mode guide for full examples.


Callbacks

All callbacks are optional. They fire regardless of whether headless is true or false — in default mode they run alongside the built-in UI; in headless mode they are your only UI trigger.


onAccessGranted · () => void

Fires when the reader gains access (either already purchased or just bought now). Equivalent to the paywall:hidden event.

onAccessGranted: () => {
analytics.track('article_unlocked');
}

onStateChange · (state: SDKState) => void

Fires on every state change. Receives the complete state snapshot. Use this as the single reactive hook for headless mode — equivalent to calling cc.subscribe() separately.

onStateChange: (state) => {
document.getElementById('spinner').hidden = !state.isLoading;
}

See State Reference for all SDKState fields.


onReady · (state: SDKState) => void

Fires once when the SDK finishes its first access check. Equivalent to the ready event.

onReady: (state) => {
if (!state.hasAccess) showPaywall();
}

onLoginRequired · () => void

Fires when the paywall is reached and the user is not logged in. Show your login UI here and call cc.login() from your button.

onLoginRequired: () => {
document.getElementById('login-prompt').style.display = 'block';
}

onPurchaseRequired · (info) => void

Fires when the user is logged in but has not yet purchased this article. Show your unlock UI here and call cc.purchase() from your button.

onPurchaseRequired: ({ requiredCredits, creditBalance }) => {
document.getElementById('cost').textContent = requiredCredits;
document.getElementById('balance').textContent = creditBalance;
document.getElementById('purchase-prompt').style.display = 'block';
}
info fieldTypeDescription
requiredCreditsnumber | nullCredits needed to unlock
creditBalancenumber | nullReader's current balance

onInsufficientCredits · (info) => void

Fires when the user is logged in but their balance is below the article price. Show a top-up UI here and call cc.buyMoreCredits().

onInsufficientCredits: ({ required, available }) => {
const needed = required - available;
document.getElementById('topup-message').textContent =
`You need ${needed} more credits.`;
document.getElementById('topup-prompt').style.display = 'block';
}
info fieldTypeDescription
requirednumberCredits needed for this article
availablenumberReader's current balance

onPurchased · (info) => void

Fires after a successful purchase. Equivalent to the article:purchased event.

onPurchased: ({ creditsSpent, remainingBalance }) => {
analytics.track('purchase', { creditsSpent, remainingBalance });
}
info fieldTypeDescription
creditsSpentnumberCredits deducted
remainingBalancenumberReader's updated balance

onUserLogin · (user: User) => void

Fires when the reader authenticates. Equivalent to the auth:login event.

onUserLogin: (user) => {
document.getElementById('welcome').textContent = `Hi, ${user.firstName}!`;
}

onUserLogout · () => void

Fires when the reader's session ends. Equivalent to the auth:logout event.

onUserLogout: () => {
location.reload();
}

onError · (info) => void

Fires when the SDK encounters an error. Equivalent to the error event.

onError: ({ message, error }) => {
console.error('[CC]', message, error);
Sentry.captureException(error);
}
info fieldTypeDescription
messagestringHuman-readable description
errorunknown | undefinedUnderlying error object

Data attribute equivalents

For CDN auto-init, these data-* attributes can be set directly on the <script> tag:

Config optionData attribute
apiKeydata-api-key
contentSelectordata-content-selector
teaserParagraphsdata-teaser-paragraphs
enableCommentsdata-enable-comments
debugdata-debug

All callback options (onStateChange, onLoginRequired, etc.), theme, articleUrl, extensionId, paywallMode, paywallTopSlot, reactDOM, and headless are JavaScript-only and have no data-* equivalent.