Publisher-Managed Access API
Publisher-managed access lets your site decide exactly what subscribers can see. Ezoic Subscriptions handles checkout, payment infrastructure, subscriber sessions, and access verification. Your code checks a product handle and shows the right content.
Product Handles
A product handle is the stable identifier your site checks. The handle is authored on your product in the Ezoic dashboard and belongs to your site.
Product handles are:
- Domain-scoped.
- Case-insensitive.
- Stored lowercase.
- Limited to letters, numbers, hyphens, and underscores.
Examples:
premiumremove-adspro
Use handles that describe the access level, not the current price or promotion.
Basic Access Check
Every integration follows the same shape: check a product handle, then deliver the paid benefit when the decision is allowed. The most common benefit is an ad-free experience.
<script>
window.ezsubscriptions = window.ezsubscriptions || {};
ezsubscriptions.cmd = ezsubscriptions.cmd || [];
ezsubscriptions.cmd.push(async function () {
const access = await ezsubscriptions.hasAccess("remove-ads");
if (access.decision === "allowed") {
// Deliver the benefit — e.g. remove ads. See Onsite Script Integration.
return;
}
ezsubscriptions.showPaywall({ product: "remove-ads" });
});
</script>
<script src="https://sm.ezoic.com/min.js" async defer></script>
Gating content works the same way — reveal a subscriber-only element instead of removing ads:
<div data-premium-content hidden>
Premium content goes here.
</div>
<script>
window.ezsubscriptions = window.ezsubscriptions || {};
ezsubscriptions.cmd = ezsubscriptions.cmd || [];
ezsubscriptions.cmd.push(async function () {
const access = await ezsubscriptions.hasAccess("premium");
if (access.decision === "allowed") {
document.querySelector("[data-premium-content]").hidden = false;
return;
}
ezsubscriptions.showPaywall({ product: "premium" });
});
</script>
<script src="https://sm.ezoic.com/min.js" async defer></script>
hasAccess(...) checks whether the current visitor has an active entitlement for the given product. showPaywall({ product }) opens Ezoic's pre-built paywall and checkout experience for that product's prices. The same product handle is what you check and what you sell. For the ad-removal specifics (ezsubscriptions.disableAds() / allowAds()), see Onsite Script Integration.
Access Decisions
hasAccess(...) returns an access decision:
allowed: The visitor has active access.login_required: The visitor is not signed in to Ezoic Subscriptions on this site.denied: The visitor is signed in but does not have access.expired: The visitor had access, but it is no longer active.revoked: Access was removed.unknown_product: The product handle is not recognized for this site — usually a typo or an inactive product.
For most integrations, show subscriber-only content only when the decision is allowed. Treat every other decision as no current access, then call showPaywall({ product: "your-product-handle" }) or show your own message before opening checkout.
Anonymous Visitors
Anonymous visitors do not require a network request for access checks:
hasAccess(...)returnslogin_required.getProducts()returns an empty list.getPurchases()returns an empty list.
This keeps pages responsive — there is no network round trip for a visitor who has no session to check yet. When an anonymous visitor decides to subscribe, showPaywall(...) handles sign-in, account creation, and checkout.
Checking Multiple Features
Use getProducts() when your site has several subscriber-only features:
const products = await ezsubscriptions.getProducts();
if (products.includes("pro")) {
enableProTools();
}
if (products.includes("premium")) {
showPremiumNavigation();
}
Listing a Visitor's Purchases
getProducts() lists the whole-product access a visitor holds, but not individual item purchases. To build a "your purchases", downloads, or library page that includes per-item purchases, use getPurchases():
const purchases = await ezsubscriptions.getPurchases();
for (const purchase of purchases) {
if (purchase.status !== "active") continue;
// purchase.productKey, purchase.item, purchase.expiresAt
}
Each entry is { productKey?, item?, status, expiresAt? }. item is set for a per-item purchase; expiresAt is set only for time-limited access. Anonymous visitors return an empty list.
One-Time Purchases
For per-item purchases — unlocking a single article, download, or other one-off item rather than granting a recurring product — check hasPurchased({ item }) instead of hasAccess:
const access = await ezsubscriptions.hasPurchased({
item: "article-12345",
});
if (access.decision === "allowed") {
document.querySelector("[data-premium-content]").hidden = false;
}
Sell the item with ezsubscriptions.openCheckout({ price: "article-unlock", item: "article-12345" }), or let Ezoic's paywall sell it with ezsubscriptions.showPaywall({ product: "premium", item: "article-12345" }). See Products, Prices, and Paid Access for how one-time prices and items work.
Subscribe or Buy This Article
To let a subscription unlock everything or a one-time purchase unlock a single article, check both and open a paywall that offers the article price alongside the subscription:
const item = "article-12345";
const [sub, bought] = await Promise.all([
ezsubscriptions.hasAccess("premium"),
ezsubscriptions.hasPurchased({ item }),
]);
if (sub.decision === "allowed" || bought.decision === "allowed") {
document.querySelector("[data-premium-content]").hidden = false;
} else {
ezsubscriptions.showPaywall({ product: "premium", item });
}
Selling the Current Article Automatically
If you'd rather not assign each article an item key, configure the one-time price to unlock the current article automatically (see Products, Prices, and Paid Access). Then showPaywall({ product }) sells access to whatever page it runs on, and you reveal an already-bought article with hasPurchased({ page: true }):
const access = await ezsubscriptions.hasPurchased({ page: true });
if (access.decision === "allowed") {
document.querySelector("[data-premium-content]").hidden = false;
}
{ page: true } checks the current page using the same page key Ezoic stamped at checkout, so you never compute or pass the item yourself.
Reacting to Access Changes
When the visitor logs in, logs out, or completes checkout, subscribe to access:change so gated UI re-renders without a page reload:
ezsubscriptions.on("access:change", async function () {
const access = await ezsubscriptions.hasAccess("premium");
document.querySelector("[data-premium-content]").hidden = access.decision !== "allowed";
});
Implementation Notes
- The product handle is what you check (
hasAccess) and what you sell (showPaywall({ product })). - Use
showPaywall({ product })afterhasAccess(...)returns anything other thanallowed. - Keep product and price handles stable after launch.
- Show teaser content to everyone, then reveal or fetch the protected body only after access is allowed.
For method options, callback hooks, and return values, see JavaScript API Reference.