Get started

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.