/ wayleave meter
Ledger Account

Getting started

Ten minutes, four steps. At the end you'll have a running ledger of every agent that crossed your app, attributed to a cryptographic identity, and exportable as something you can invoice from.

Before anything else: nothing here charges you, and nothing here can block a request. The middleware is free and open source. The meter is $49/month only once you choose to start a plan — signing in does not start one.

1 Install the middleware

Zero dependencies. It uses Node's own crypto and pulls nothing into your tree.

npm install wayleave

Requires Node 18 or newer.

2 Get an API key

Go to AccountIssue key. Give it a label like production. The key is shown once and never again — we store only a hash of it, so if you lose it you issue a new one rather than recovering the old.

Give staging its own key. If one leaks you can revoke it without stopping your production billing.

3 Start in observe-only mode

Do not price anything yet. Run it for a few days with no prices and no deny rules, and look at what actually crosses your app. Almost everyone is surprised, and you cannot price sensibly until you know what is out there.

import Wayleave from 'wayleave';
import { MeterSink } from 'wayleave/meter';

const sink = new MeterSink({
  endpoint: 'https://meter.wayleave.dev',
  apiKey: process.env.WAYLEAVE_METER_KEY,
  onError: (err, info) => console.warn('[wayleave]', err.message, info),
});

const gate = new Wayleave({
  pricedPaths: {},   // observe only — nothing is charged, nothing is blocked
  rules: {},         // no deny rules — measure first, police later
  sink,
});

app.use(gate.express());        // mount BEFORE your auth middleware

process.on('SIGTERM', () => sink.close());   // drain on shutdown

Mount it before authentication. After your auth layer you only see traffic that already had a key, which is exactly the traffic you do not need to measure.

Always call sink.close() on shutdown. Without it, whatever is buffered when your container cycles is data you never receive.

Check it is observing and not interfering:
curl -i -A 'GPTBot/1.0' https://your-app.com/health   # expect 200
If this integration ever returns 402 or 403, it is misconfigured. In observe-only mode nothing should ever be refused.

4 Read your ledger

Within a minute, crossings appear on your ledger. Four numbers matter:

Unbilled agent crossings
Agents that crossed and produced no revenue. This is the big number, and it is the point: it is what you are currently giving away.
Turned away at a 402
Agents that asked for a priced route and never paid. Most crawlers have no wallet today, so they are refused rather than served free. You are protected, not paid — and the ledger shows exactly how much demand is standing at your gate.
Collectable
Money a payment rail actually confirmed. Invoice this number and no other. It will be small at first. That is honest, not broken.
Unconfirmed
Your middleware booked it as billed, but no rail confirmed it. Usually settlement lag. If it stays high, your verifyPayment is trusting something it should not — do not invoice it.

Only verified_agent rows are marked invoiceable. A self-declared bot is an identity you cannot defend on an invoice, so we show it as observed only.

5 When you're ready, charge

Two changes. Both matter.

const gate = new Wayleave({
  pricedPaths: { '/api/premium': 0.05 },   // 5¢ per crossing

  // REQUIRED on anything priced. Without it, a bot that sends a browser
  // user-agent and an accept-language header is classified as human and
  // crosses free — that is two headers, and it is the whole bypass.
  strictPricedPaths: true,
  confirmHuman: req => Boolean(req.session?.userId),   // your session, never a header

  // REQUIRED to sell anything. Defaults to deny: configure nothing and
  // priced routes stay 402 forever, deliberately.
  verifyPayment: (proof, ctx) => facilitator.confirmSettled(proof, ctx),

  sink,
});

Set your receiving address too. Agents pay a facilitator and the facilitator pays that address. Wayleave never holds your funds and is never a step in the path — we record that money moved, we do not move it.

What this does not do

Worth knowing before you rely on it.

If something looks wrong

No crossings appear
Check the key is right (401 in your logs via onError), that you mounted before auth, and that at least one flush interval has passed. The sink batches; it is not instant.
Numbers look doubled
They should not be. Every event carries an idempotency key and the meter refuses repeats, so retries are safe — send the same batch as many times as you like. If you genuinely see doubles, tell us; that is a bug in us, not in you.
Collectable is zero but agents are paying
You have no receiving address set, or your verifyPayment is not wired to a real facilitator. We only count settlement a rail confirmed.
Real users are getting 402
You turned on strictPricedPaths without a working confirmHuman. It fails closed on purpose. Point it at your session, never at a header.