JavaScript API Reference
The onsite script exposes window.ezsubscriptions after https://sm.ezoic.com/min.js loads. Because the script loads asynchronously, put API calls inside the cmd queue:
<script>
window.ezsubscriptions = window.ezsubscriptions || {};
ezsubscriptions.cmd = ezsubscriptions.cmd || [];
ezsubscriptions.cmd.push(function (api) {
// api is the resolved ezsubscriptions API.
});
</script>
<script src="https://sm.ezoic.com/min.js" async defer></script>
Methods that return promises can reject if a network request fails. Wrap access checks and checkout-launching calls in try / catch when your page needs a custom fallback.
Most calls take a product handle or a price handle — the stable keys you author on a product and its prices in your Ezoic dashboard. See Products, Prices, and Paid Access for how those keys are created.
login(), logout(), initialize(), and authChanged(). See Visitor Authentication for the BYO handling.
Readiness
ezsubscriptions.cmd
Queues callbacks until the script is ready. Each callback receives the resolved API object.
ezsubscriptions.cmd.push(function (api) {
api.showPaywall({ product: "remove-ads" });
});
Callbacks pushed after the script is ready run immediately.
ezsubscriptions.ready
Boolean value that is true after the API is ready.
if (window.ezsubscriptions?.ready) {
window.ezsubscriptions.showPaywall({ product: "remove-ads" });
}
Access Methods
hasAccess(query)
hasAccess(query: string | { product: string }): Promise<AccessResponse>
Checks whether the current visitor holds an active entitlement for a product. Pass a product handle — the whole-product access key, not a price handle or an item key.
const access = await ezsubscriptions.hasAccess("remove-ads");
You can pass the product handle directly or as an object:
await ezsubscriptions.hasAccess("remove-ads");
await ezsubscriptions.hasAccess({ product: "remove-ads" });
Parameters:
query: string | { product: string }— the product handle authored on your product.
Returns:
{
decision: "allowed" | "denied" | "login_required" | "expired" | "revoked" | "unknown_product",
reasonCode: string
}
Show protected content only when decision is allowed. Treat every other decision as no current access.
Anonymous visitors return login_required without a network request.
This promise can reject if the visitor is signed in and the access request fails.
hasPurchased(query)
hasPurchased(query: { item: string } | { page: true }): Promise<AccessResponse>
Checks whether the current visitor has bought a one-time per-item purchase. The item is a site-wide key matched on its own — no price needed. It is the read counterpart to openCheckout({ price, item }) and showPaywall({ product }).
// A single item you name:
const access = await ezsubscriptions.hasPurchased({ item: "article-12345" });
// The current article (a price set to unlock the current article automatically):
const access = await ezsubscriptions.hasPurchased({ page: true });
if (access.decision === "allowed") {
revealArticle();
}
Parameters:
query: { item: string } | { page: true }query.item— the item key you passed toopenCheckout({ price, item })/showPaywall({ product, item }). It is matched exactly, across the visitor's one-time purchases on the site, independent of which price sold it.query.page— pass{ page: true }instead of anitemto check the current page. Ezoic resolves the same page key it stamped at checkout for a price set to unlock the current article automatically, so you never compute it yourself.
Returns the same AccessResponse shape as hasAccess.
Anonymous visitors return login_required without a network request. A missing item returns denied without a network request.
getProducts()
getProducts(): Promise<string[]>
Returns the product handles the visitor currently holds.
const products = await ezsubscriptions.getProducts();
if (products.includes("pro")) {
enableProFeatures();
}
Parameters: none.
Returns:
string[]
Anonymous visitors return [] without a network request.
This promise can reject if the visitor is signed in and the request fails.
getPurchases()
getPurchases(): Promise<Purchase[]>
Returns the visitor's purchases — for building a "your purchases", downloads, or library page. Unlike getProducts(), the list includes one-time per-item purchases.
const purchases = await ezsubscriptions.getPurchases();
purchases
.filter((purchase) => purchase.status === "active")
.forEach((purchase) => renderLibraryRow(purchase));
Parameters: none.
Returns:
Array<{
productKey?: string,
item?: string,
status: string,
expiresAt?: string
}>
productKeyis the product the purchase belongs to. It is omitted if that product was removed.itemis set only for a one-time per-item purchase.statuslets you filter active access from expired or revoked entitlements.expiresAtis set only for time-limited access; lifetime access omits it.
Anonymous visitors return [] without a network request. This promise can reject if the visitor is signed in and the request fails.
Ad Removal Methods
An ad-free experience is the most common paid benefit. After hasAccess(...) returns allowed, call disableAds(); when access is lost, call allowAds(). The widget owns the whole mechanism — you never touch ad placeholders or cookies. See Removing Ads for Subscribers for the full pattern.
disableAds()
disableAds(): Promise<void>
Suppresses every ad format for an entitled visitor — display, floating video, and interstitials. The widget mints a short-lived signed token and stores it in a reserved first-party cookie that Ezoic reads server-side, so ads never load rather than being torn down after they render.
const access = await ezsubscriptions.hasAccess("remove-ads");
if (access.decision === "allowed") {
ezsubscriptions.disableAds();
}
Parameters: none.
Returns:
Promise<void>
On the page where access is first gained (typically right after checkout or login), disableAds() reloads once so the server can apply suppression; every page view after that is ad-free with no reload. When access is gained inside checkout, the reload waits until the visitor closes the confirmation screen; for a mid-article login, their scroll position is preserved. If the visitor is not entitled or the token cannot be minted, it fails open and leaves ads in place.
allowAds()
allowAds(): void
Removes the ad-suppression cookie when access is lost — logout, an expired subscription, or a revoked entitlement. It does not reload; ads return on the visitor's next page view.
const access = await ezsubscriptions.hasAccess("remove-ads");
if (access.decision !== "allowed") {
ezsubscriptions.allowAds();
}
Parameters: none.
Returns: void.
Paywall and Checkout Methods
showPaywall(options)
showPaywall(options: {
product: string;
item?: string;
dismissible?: boolean;
onSuccess?: (result: CheckoutResult) => void;
onCancel?: () => void;
onError?: (error: CheckoutError) => void;
}): Promise<void>
Opens Ezoic's pre-built paywall and checkout experience for a product and its active prices. It takes a product handle (what you sell), not a price handle; item scopes a one-time single-item purchase and is matched later by hasPurchased({ item }).
await ezsubscriptions.showPaywall({
product: "remove-ads",
onSuccess: function (result) {
window.location.reload();
},
onError: function (error) {
console.log(error.message);
},
});
Parameters:
options?: objectoptions.product: string— the product handle to present. Required: without it the call does nothing.options.item?: string— opts a one-time single-item price into the paywall and scopes the purchase to that item (matched later byhasPurchased({ item })). Without it, single-item prices that need a publisher-supplied item are hidden; a price set to unlock the current article automatically always appears and uses the page URL as the item.options.dismissible?: boolean— whentrue, the visitor can close the paywall andonCancelcan fire. When omitted orfalse, the paywall is blocking.options.onSuccess?: (result: CheckoutResult) => void— fires after checkout completes and access is established.options.onCancel?: () => void— fires when a dismissible paywall is closed before checkout completes.options.onError?: (error: CheckoutError) => void— fires when a checkout attempt fails. It may fire more than once if the visitor retries.
Returns:
Promise<void>
The widget loads the product's active prices, re-checks visitor access, and renders nothing if the visitor already holds the product or the product has no active prices to sell. If the product config cannot be loaded, the widget shows a dismissible error message and fires onError.
openCheckout(options)
openCheckout(options: {
price: string;
item?: string;
dismissible?: boolean;
onSuccess?: (result: CheckoutResult) => void;
onCancel?: () => void;
onError?: (error: CheckoutError) => void;
}): Promise<void>
Launches checkout directly for a single price handle, skipping the paywall's product and price selection. Use it for custom "buy" buttons wired to a specific price. item scopes a one-time single-item purchase; it is only meaningful for an item-scoped one-time price.
await ezsubscriptions.openCheckout({
price: "remove-ads-monthly",
onSuccess: function (result) {
window.location.reload();
},
});
Parameters:
options: objectoptions.price: string— the price handle to charge. Required: without it the call does nothing.options.item?: string— scopes a one-time per-item purchase; the value is stored verbatim as the entitlement key and matched exactly byhasPurchased({ item }). Optional for a product-scoped price, but required for an item-scoped price — checkout is rejected without it.options.dismissible?: boolean— whentrue, the visitor can close checkout andonCancelcan fire.options.onSuccess?: (result: CheckoutResult) => voidoptions.onCancel?: () => voidoptions.onError?: (error: CheckoutError) => void
Returns:
Promise<void>
Login and Logout
login(options)
login(options?: { dismissible?: boolean }): Promise<void>
Opens a login surface so a returning subscriber can sign in without first hitting a paywall — wire it to a "Log in" link in your own navigation. On a successful login the widget establishes the access session and fires access:change, so gated content unlocks in place with no page reload.
document.getElementById("log-in").addEventListener("click", function () {
ezsubscriptions.login();
});
login() is mode-aware:
- Ezoic visitor accounts — opens Ezoic's login screen in the widget (email and password, Continue with Google when enabled, and the one-time email sign-in link).
- Bring your own login — forwards to your adapter's
goToLogin(); the widget renders no login UI of its own. See Visitor Authentication for the adapter setup and theauthChanged()call that goes with it.
You can also trigger it declaratively, with no JavaScript, using the data-ezoic-login attribute.
Parameters:
options?: objectoptions.dismissible?: boolean— whether the visitor can close the login surface. Defaults totrue.
Returns:
Promise<void>
If the visitor already has an active session, login() does nothing (and re-fires access:change). On a bring-your-own-login domain with no registered adapter, it is a no-op.
logout()
logout(): Promise<void>
Ends the visitor's access session — the counterpart to login(). It clears the Subscriptions access session and fires access:change so gated content re-locks in place. Wire it to a "Log out" link, or use the declarative data-ezoic-logout attribute.
document.getElementById("log-out").addEventListener("click", function () {
ezsubscriptions.logout();
});
ezsubscriptions.disableAds(), logout() restores ads too. Because ad suppression is applied on Ezoic's servers, a page refresh may be needed before ads begin showing again.
logout() is mode-aware:
- Ezoic visitor accounts — also signs the visitor out of their Ezoic visitor account behind the scenes, so they are not silently re-authenticated on the next page load.
- Bring your own login — the widget cannot end your own login session, so sign the visitor out of your system first, then call
logout(). CallingauthChanged()after your sign-out also clears the access session, but unlikelogout()it does not restore ads or discard a parked checkout.
Returns:
Promise<void>
See Signing Out for the full lifecycle across both modes. The subscriber portal (subscriber.ezoic.com) has its own separate sign-out.
Authentication Methods
These methods apply to Bring Your Own Login integrations. See Visitor Authentication for the full setup, including the auth adapter contract. Sites on Ezoic visitor accounts do not need them.
initialize(config)
initialize(config: { auth: AuthAdapter }): void
Registers your auth adapter so the widget can resolve the logged-in visitor's identity from your own login system.
ezsubscriptions.initialize({
auth: {
getUserEmail: () => currentUser?.email ?? null,
goToLogin: () => location.assign("/login"),
goToCreateAccount: () => location.assign("/signup"),
},
});
Parameters:
config: { auth: AuthAdapter }— the adapter object. See Visitor Authentication for the adapter method contract.
Returns: void.
Safe to call repeatedly; a later valid call replaces the adapter.
authChanged()
authChanged(): Promise<void>
Signals that your auth state changed — a login, logout, account switch, or async session restore. The widget re-resolves identity through the adapter, refreshes or clears its session, and fires access:change.
await ezsubscriptions.authChanged();
Parameters: none.
Returns:
Promise<void>
Call this instead of re-running initialize for runtime auth changes.
on(event, handler) / off(event, handler)
on(event: "access:change", handler: () => void): () => void
off(event: "access:change", handler: () => void): void
Subscribe to access:change so gated UI re-renders when the visitor's access may have changed (login, logout, account switch, completed checkout).
const unsubscribe = ezsubscriptions.on("access:change", async function () {
const access = await ezsubscriptions.hasAccess("remove-ads");
if (access.decision === "allowed") {
ezsubscriptions.disableAds();
} else {
ezsubscriptions.allowAds();
}
});
// Later, on cleanup:
unsubscribe();
Parameters:
event: "access:change"— the only supported event.handler: () => void— runs when access may have changed. Takes no arguments; re-read access inside it.
on returns an unsubscribe function. off(event, handler) detaches a handler by reference and returns void.
Account Methods
openAccountPortal()
openAccountPortal(): void
Opens the subscriber portal at https://subscriber.ezoic.com in a new tab, where a logged-in member manages payment methods, receipts, and subscriptions. Wire this to a "Manage subscription" link for members.
document.getElementById("manage-subscription").addEventListener("click", function () {
ezsubscriptions.openAccountPortal();
});
Parameters: none.
Returns: void.
This is plain navigation; the portal runs its own sign-in.
Donation Methods
openDonation(options)
openDonation(options?: {
amountCents?: number;
productId?: string;
onSuccess?: (result: CheckoutResult) => void;
onCancel?: () => void;
onError?: (error: CheckoutError) => void;
}): Promise<void>
Opens the donation checkout. This is the single donation entry point: it loads your donation settings the first time it's called, so no setup call is required and it works no matter when you call it. On a site with no donations configured it does nothing (and logs a warning). A [data-ezoic-donate] element opens the same dialog with no JavaScript.
ezsubscriptions.openDonation({
amountCents: 2500,
onSuccess: function (result) {
console.log("Donation complete", result.amountCents);
},
});
Parameters:
options?: objectoptions.amountCents?: number— preselected donation amount in cents. For example,2500means$25.00. The configured minimum still applies.options.productId?: string— optional donation product ID. Most donation integrations should omit this because a site has one active donation in the current dashboard flow.options.onSuccess?: (result: CheckoutResult) => void— fires after donation checkout completes.options.onCancel?: () => void— fires when the visitor closes the donation dialog before checkout completes.options.onError?: (error: CheckoutError) => void— fires when a checkout attempt fails. It may fire more than once if the visitor retries.
Returns:
Promise<void>
If amountCents is missing, invalid, or below the configured minimum, the widget falls back to the normal donation picker.
closeDonation()
closeDonation(): void
Closes the donation dialog.
ezsubscriptions.closeDonation();
Parameters: none.
Returns: void.
showDonations(options)
showDonations(options?: {
onSuccess?: (result: CheckoutResult) => void;
onCancel?: () => void;
onError?: (error: CheckoutError) => void;
}): Promise<void>
Optional. openDonation() and [data-ezoic-donate] triggers work on their own, so most sites never need this. Its one use is to set page-wide default callbacks that fire for donations opened without their own — chiefly [data-ezoic-donate] buttons, which can't carry callbacks inline. Callbacks passed to openDonation() take precedence over these defaults.
await ezsubscriptions.showDonations({
onSuccess: function (result) {
console.log("Donation complete", result.amountCents);
},
});
Parameters:
options?: objectoptions.onSuccess?: (result: CheckoutResult) => void— default success handler for donations opened without their own.options.onCancel?: () => void— default cancel handler.options.onError?: (error: CheckoutError) => void— default error handler.
Returns:
Promise<void>
Surface Control
hide()
hide(): void
Unmounts the paywall, checkout, and donation surfaces.
ezsubscriptions.hide();
Parameters: none.
Returns: void.
Callback Payloads
CheckoutResult
Passed to onSuccess.
{
productType?: "subscription" | "one_time" | "donation",
productExternalId?: string,
priceExternalId?: string,
amountCents?: number,
product?: string,
price?: string,
item?: string
}
amountCentsis the requested base amount in cents. Taxes, discounts, or payment-provider adjustments can change the final charged total. It is omitted for a per-item purchase, whose amount is set server-side.productis set when checkout was opened withshowPaywall({ product }).priceis set when checkout was opened withopenCheckout({ price }).itemis set for a one-time per-item purchase —openCheckout({ price, item }),showPaywall({ product, item }), or a paywall price that unlocks the current article automatically.- A callback that throws is logged and does not break the widget.
CheckoutError
Passed to onError.
{
message: string
}
Checkout stays open for retry after an error, so onError can fire more than once.