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 with hasAccess(...) and sells with showPaywall({ product }). You author it on the product in your Ezoic dashboard. See Product Handles for the naming rules and examples.
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. See Gate Premium Content for that variant.
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.
A non-allowed decision is never a reason to sign the visitor out. denied in particular means the visitor is signed in and ready to buy — calling logout() there destroys the session their checkout depends on.
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 identity and checkout. With expedited checkout on (the default), a guest pays on a single screen with no separate sign-in step.
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.
Granting Access Manually
You can comp a reader access to a product without a payment — for support make-goods, sponsors, or staff. In the Ezoic dashboard, open Subscriptions → Access, click + Grant access, and enter:
- Email — the reader's email address. It identifies (or creates) the subscriber.
- Product — the product to grant.
- Access days (optional) — leave blank for access that does not expire, or enter a number of days for a time-limited grant.
A manual grant charges nothing and creates no subscription — the reader simply passes hasAccess(...) for that product until the grant expires or is revoked. If the reader already holds active access, the grant is a no-op.
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";
});
hasAccess(...) on a setInterval. Login, logout, account switches, and completed checkouts all fire access:change, so a timer only adds requests without catching anything the event misses.
Implementation Notes
- 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 every method's options and callbacks, see JavaScript API Reference.