Quickstart
See who’s crossing, in three lines
Install, mount, watch. Add rules and pricing only when you know what is actually knocking.
What it does before you configure anything
Wayleave sorts every request into one of four lanes: verified agent (a valid Web Bot Auth signature), declared agent (says it is a bot), suspected bot (looks automated), or human. Nothing is blocked and nothing is charged until you say so. People always pass free.
1 · Install
npm install wayleave
Node 18 or newer. Zero dependencies — Node’s own crypto and nothing else.
2 · Mount the gate
import { Wayleave } from 'wayleave';
const gate = new Wayleave({
meter: { apiKey: process.env.WAYLEAVE_METER_KEY }, // optional
});
app.use(gate.express()); // before your routes
Get a Meter key from your dashboard.
Without one the gate still classifies every request; you just will not see it
anywhere. import Wayleave from 'wayleave' works too — the class is both
the default and a named export.
3 · Watch for a few days
Open the dashboard. You will see requests by lane, the identities behind them, and which routes agents actually want. Most sites find far more agent traffic than they expected, and almost none of it signed.
4 · Then add terms
Price a route for agents
const gate = new Wayleave({
pricedPaths: { '/api/premium': 0.05 }, // USD per request
strictPricedPaths: true, // a bot cannot just claim to be a browser
confirmHuman: req => Boolean(req.session?.user), // your signed-in people pass free
verifyPayment: coinbaseFacilitator({ /* your keys */ }),
});
Agents get HTTP 402 with an x402 challenge until they pay. Money settles
to your address; Wayleave never holds it. Without verifyPayment, priced routes
stay at 402 for agents — the safe default, because a payment that cannot be
verified must not buy anything.
Strict mode and your own users. Turning
strictPricedPaths on without a confirmHuman means nobody qualifies as a
person on a priced route — including your customers. The library warns you at
startup if you do this. People never paying is the rule the whole thing is
built on, so the combination that breaks it is never silent.
Limit or deny by lane
rateLimits: { declared_agent: 60 }, // per minute
rules: { suspected_bot: [['/api', false]] }, // deny suspected bots under /api
Frameworks other than Express
const { status, lane, why } = await gate.handleAsync(req);
Use handleAsync() whenever you have configured a remote policy or a
verifyPayment that talks to a network — both are asynchronous, and the
synchronous handle() cannot await either. gate.express() already uses the
async path.
On 0.5.2, put the path on req.path.
handle() and handleAsync() read req.path, which is an Express property.
If you hand them a raw Node, Fastify or Hono request — where the path lives on
req.url — no priced prefix and no rule will match, and you will get
200 on a route you priced. Pass { method, path, headers } explicitly until
0.5.3 ships, which reads both.
No install? Use the Identity API
From any language, one call:
curl -X POST https://api.wayleave.dev/v1/identify \
-H "Authorization: Bearer $WAYLEAVE_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://yourapi.com/data","headers":{"user-agent":"GPTBot/1.0"}}'
{ "lane": "declared_agent", "verified": false,
"reasons": ["self-identified in user-agent", "no signature presented"] }
Full details in the API reference.
SDKs
Thin clients over the same API, each with no dependencies beyond its standard library:
# Python
pip install wayleave
# Cloudflare Workers, Vercel Edge, Deno, Bun
npm i @wayleave/edge
# Go
go get github.com/gibrancorbin11-hub/wayleave-go
from wayleave import Client
from wayleave.middleware import WayleaveWSGI
c = Client(os.environ['WAYLEAVE_API_KEY'])
r = c.identify('https://api.you.com/data', request_headers, ip=client_ip)
r.lane, r.verified, r.operator # 'verified_agent', True, 'Example Labs'
# Flask or Django
app.wsgi_app = WayleaveWSGI(app.wsgi_app, c, block=('suspected_bot',))
import { createClient, withWayleave, getWayleave } from '@wayleave/edge';
const client = createClient({ apiKey: env.WAYLEAVE_API_KEY });
export default { fetch: withWayleave(async (req) => {
const r = getWayleave(req); // { lane, verified, identity, reasons }
return new Response(r.verified ? 'hello, verified agent' : 'hello');
}, { client, block: ['suspected_bot'], ipHeader: 'cf-connecting-ip' }) };
c := wayleave.NewClient(os.Getenv("WAYLEAVE_API_KEY"))
http.Handle("/", wayleave.Middleware(c, wayleave.Options{Block: []string{"suspected_bot"}})(mux))
All three fail open and none of them can be configured to block a person:
passing human to a middleware’s block list raises rather than being
quietly obeyed. Any other language talks to the same API with the
curl above.
Build a whole app instead
wayleave.dev/app/build — describe what you want, and get a repository you own with the gate already mounted. Templates today: a paid API, an observe-only API, and a full-stack app with accounts, a database, a map view and saved items.
The rules Wayleave never breaks
- People are never charged and never blocked.
- If Wayleave is unreachable, your app keeps serving. Access fails open.
- If a payment cannot be verified, a priced route stays at 402. Money fails closed.
- Identity comes only from signature math, never from a header that claims it.
- Wayleave never holds your funds and takes no percentage.