Guides
SDK (any framework)
One middleware file — verivyxProxy (Next), verivyxMiddleware (Express), or verivyxHonoMiddleware (Hono) — gates your whole app. Humans read for free, verified search crawlers get an SEO preview, AI agents pay via x402, and your content never leaves your server unless Verivyx confirms the request is authorised.
Authorize-only model
The SDK does not proxy or cache your content. It inspects every incoming request, checks the caller's identity and payment proof with the Verivyx API, and either lets the request reach your app or answers it itself (a 402 for unpaid agents, a teaser for crawlers, an unlock page for humans). Verivyx only ever sees your site token, the route slug, and the proof-of-payment or verification token — the full resource body stays on your server. Unauthorised callers never reach the route that renders it, so scrapers and bots never receive the content.
Get your site token first
Before adding the SDK, grab a VERIVYX_TOKEN from the dashboard. The token alone identifies your site and authorises the SDK to call the Verivyx API on your behalf — there is no domain entry and no DNS verification step.
The flow: sign up, set your payout wallet and price, then copy your site token and add the middleware. You can re-issue the token any time.
Recommended: one middleware file
The simplest integration is a single middleware that gates every matched route — no per-route code. Install the adapter for your framework and add one file. The middleware is the authoritative gate: it withholds content from bots (they never reach the page), settles agent x402 payments inline, lets verified humans through, and serves crawlers an SEO preview.
Next.js (App Router)
npm i @verivyx/paywall-nextAdd a proxy.ts at your project root:
// proxy.ts — one file gates your whole Next.js app.
import { verivyxProxy } from "@verivyx/paywall-next";
export const proxy = verivyxProxy({
token: process.env.VERIVYX_TOKEN, // required — your site token (or set VERIVYX_TOKEN env)
match: ["/articles/:path*"], // which paths to gate
seoPreview: ({ slug }) => { // teaser for crawlers + humans
const a = getArticleSync(slug);
return { title: a.title, excerpt: a.excerpt };
},
humanUnlock: {}, // humans solve an in-page PoW → read free
});
export const config = { matcher: ["/((?!_next/|favicon.ico).*)"] };Express
npm i @verivyx/paywall-expressimport express from "express";
import { verivyxMiddleware } from "@verivyx/paywall-express";
const app = express();
app.set("trust proxy", true);
app.use(verivyxMiddleware({
token: process.env.VERIVYX_TOKEN, // required — your site token (or set VERIVYX_TOKEN env)
match: ["/articles/*"],
seoPreview: ({ slug }) => ({ title: titleFor(slug), excerpt: excerptFor(slug) }),
humanUnlock: {},
}));
app.get("/articles/:slug", (req, res) => res.send(renderArticle(req.params.slug)));
app.listen(3000);Hono (Cloudflare Workers / Vercel Edge)
npm i @verivyx/paywall-honoimport { Hono } from "hono";
import { verivyxHonoMiddleware } from "@verivyx/paywall-hono";
const app = new Hono();
app.use("*", verivyxHonoMiddleware({
token: process.env.VERIVYX_TOKEN, // required — your site token (or set VERIVYX_TOKEN env)
match: ["/articles/*"],
seoPreview: ({ slug }) => ({ title: titleFor(slug), excerpt: excerptFor(slug) }),
humanUnlock: {},
}));
app.get("/articles/:slug", (c) => c.html(renderArticle(c.req.param("slug"))));
export default app;For Workers, set the secret with wrangler secret put VERIVYX_TOKEN, then wrangler deploy.
Alternative: per-route handler
Prefer to gate a single API route instead of the whole app? Wrap one handler with vx.protect(handler). Same gate, scoped to that route — handy for a paid JSON endpoint:
import { verivyxNext } from "@verivyx/paywall-next";
const vx = verivyxNext(); // reads VERIVYX_TOKEN from env
// Per-route alternative: wrap a single handler instead of using middleware.
export const GET = vx.protect(
async (_req, ctx) => Response.json(await getArticle((await ctx.params).slug)),
{ seoPreview: ({ slug }) => ({ title: titleFor(slug), excerpt: excerptFor(slug) }) },
);The Express and Hono adapters expose the same vx.protect(handler) for a single route.
Humans read for free (humanUnlock)
Set humanUnlock: {} and an unverified human in a real browser gets a small in-page proof-of-work challenge that auto-solves in their browser, sets a short-lived session, and reveals the full article — no payment, no login. Crawlers still get the SEO teaser; non-browser clients and AI agents still get a 402. This is a deterrent against casual scrapers, not a hard wall — the unspoofable guarantees are payment (x402) and signed agents (Web Bot Auth).
humanUnlock, unverified humans get the static teaser (the seoPreview) — useful as a soft paywall. With it, they can unlock the full content.Configuration
The required config comes from environment variables (or pass any option to the factory):
VERIVYX_TOKEN=… # required — server-only secret, never ship to the browser
VERIVYX_API_BASE=https://api.verivyx.com # optional (this is the default)
VERIVYX_DOMAIN=example.com # optional — legacy/analytics label only; the token identifies your site| Option | Type | Default | Description |
|---|---|---|---|
token / VERIVYX_TOKEN | string | — | Required. Your site token from the dashboard — it alone identifies your site. Server-only — never expose to the browser. |
domain / VERIVYX_DOMAIN | string | "" | Optional. Legacy/analytics label only (e.g. example.com); not required and not part of onboarding — the token identifies your site. |
match | string[] | [] | Glob patterns for paths to gate. When empty, nothing is gated — set at least one. Env VERIVYX_MATCH accepts a comma-separated list. |
seoPreview | ({ slug }) => { title, excerpt } | — | Teaser served to crawlers (and to unverified humans without humanUnlock), wrapped in anti-cloaking JSON-LD. |
humanUnlock | { authBase? } | — | When set, unverified human browsers get an in-page PoW unlock to read the full content free. |
failMode / VERIVYX_FAIL_MODE | teaser | open | closed | teaser | Behaviour when the Verivyx backend is unreachable (see below). |
timeoutMs / VERIVYX_TIMEOUT_MS | number | 800 | Timeout (ms) for the quick classify/requirements call that decides how a caller is handled. |
settleTimeoutMs / VERIVYX_SETTLE_TIMEOUT_MS | number | 60000 | Timeout (ms) for the authorize/settle call that awaits the on-chain payment. Kept separate so a paying agent is never aborted mid-settle. |
timeoutMs (default 800) bounds the fast classify call for humans and crawlers, while settleTimeoutMs (default 60000) covers the x402 authorize/settle path that waits for on-chain confirmation (~15s). Because the settle path has its own generous timeout, you do not need to raise timeoutMs when you accept agent payments — a paying agent is no longer aborted mid-settle.failMode behaviour
| failMode | Behaviour when the backend is unreachable |
|---|---|
teaser | Serve the seoPreview (if configured) or a 402. Protects revenue while keeping the page indexable. |
open | Pass the request through to your app unconditionally. Use only when availability outweighs monetisation. |
closed | Return 503. Use for high-value content where accidental open access is unacceptable. |
How different callers are handled
- Humans (real browsers) — with
humanUnlock, they get an in-page PoW unlock → full content free. Without it, they get theseoPreviewteaser. A returning human with a valid session passes straight through. - Verified search crawlers— Googlebot, Bingbot, and others on Verivyx's IP-range allowlist receive the
seoPreview(title, excerpt, JSON-LDisAccessibleForFree) rather than the full body — satisfying Google's anti-cloaking rules. - AI agents / machine clients — receive
402 Payment Requiredwith x402 payment requirements. Agents that implement x402 settle a USDC micropayment on-chain and retry; the SDK verifies the proof and admits the request. - Paid / verified requests — requests carrying a valid payment proof or human session receive the full resource, exactly as if the SDK were not there.
Security note
VERIVYX_TOKEN server-only. Never include it in client bundles, expose it via NEXT_PUBLIC_, or log it. It is a bearer credential that identifies your site. If it leaks, re-issue it from the dashboard — old tokens are invalidated the moment a new one is issued.