402tools402 docs
docs · live

Paywall vs proxy

When you publish an endpoint, you pick a mode field. Two values are supported. The choice affects the take rate, the network path of the request, and the level of integration on your side.

#Comparison

| | Paywall (3 %) | Proxy (4 %) | |--------------------------|-----------------------------------------------------------------------------------|---------------------------------------------------------------------------------| | Take rate | 3 % of the call price | 4 % of the call price | | Network path | Buyer ↔ your URL (direct after 402 dance) | Buyer ↔ tools402 ↔ your URL (we relay each call) | | What tools402 returns | An Ed25519-signed JWT (60 s TTL) the buyer presents to your URL | The proxied response from your URL, augmented with X-Tools402-Tx header | | Your verification step | Verify the JWT signature (see code sample below) | None — we authenticated the payment before relaying | | Body size limit | None (your infra) | 50 MB body, 30 s timeout (our relay limits) | | Visibility on us | We see only the 402 dance + the JWT-mint moment. Your responses don't pass us. | Every request + response transits our infra (in memory, not stored) | | Best for | Sellers who want lowest take rate and don't mind a verify-JWT step | Sellers who want zero infra setup, cheap endpoints, or no JWT verification code |

#Paywall mode — flow

text
buyer ──POST──→ tools402 (/v1/<your-slug>/<path>)
              ←─402 quote─
buyer ──pays USDC on-chain──→
buyer ──POST + X-Payment──→ tools402
              ←─{ jwt, upstream_url, expires_in_seconds: 60 }─
buyer ──POST + Authorization: Bearer <jwt>──→ YOUR-URL (direct)
              ←─your 200 OK response (you never see tools402)─

You verify the JWT on your side. Reference Ed25519 public key (rotates, always live at) :

text
https://tools402.dev/.well-known/audit-pubkey.pem

Verification snippet (Node + jose) :

ts
import { jwtVerify, importSPKI } from "jose";

const pubkeyPem = await (await fetch("https://tools402.dev/.well-known/audit-pubkey.pem")).text();
const key = await importSPKI(pubkeyPem, "EdDSA");

export async function verifyToolsToken(jwt: string) {
  const { payload } = await jwtVerify(jwt, key, { algorithms: ["EdDSA"] });
  // payload = { iss: "tools402", endpoint, payer, amount_atomic, iat, exp }
  return payload;
}

The JWT TTL is 60 seconds — short enough that a stolen JWT is unusable after a minute. If the buyer's clock drifts, they pay again. The JWT's endpoint field MUST match the URL they're calling on your side.

#Proxy mode — flow

text
buyer ──POST──→ tools402 (/v1/<your-slug>/<path>)
              ←─402 quote─
buyer ──pays USDC on-chain──→
buyer ──POST + X-Payment──→ tools402
                            tools402 ──POST + X-Tools402-Tx──→ YOUR-URL
                                                             ←─your response─
              ←─your response (proxied)─

Limits :

  • Body size : 50 MB (request OR response). Larger requests fail with 413 Payload Too Large.
  • Timeout : 30 s from tools402 → your URL. Longer responses fail with 504 Gateway Timeout.
  • 5xx upstream : if your URL returns 5xx, tools402 returns 502 Bad Gateway and the buyer's payment is logged to Sentry but not refunded. Set up your monitoring so this never happens — the marketplace flips your listing to degraded if 5xx rate exceeds 50 % over 10 minutes.

#Pick paywall if

  • You already have HTTPS hosting and don't want another layer
  • Your endpoint streams large responses or processes long-running jobs (no 30 s cap, no 50 MB cap on your side)
  • You want the lowest take rate ($0.0003 vs $0.0004 on a $0.01 call — adds up at scale)
  • You're comfortable adding a tiny JWT verifier (~5 lines of jose)

#Pick proxy if

  • You want zero seller-side code beyond the upstream URL
  • Your endpoint is static / cheap / fast (well under 50 MB and 30 s)
  • You don't want to deal with JWT verification logic
  • You're testing the marketplace and want the simplest path

You can switch modes later by updating the endpoint (signed by your wallet).