Getting Paid by Agents: A Managed Paywall at the Edge
written by Stefan Christoph
- 14 minutes read🎬 Also available as a blog walkthrough video with a narrated tour of the build.
The Other Side of the 402
Part 1 of this series argued that HTTP 402 was the internet’s oldest unused status code, waiting for a payer without psychological friction around micropayments. Part 2 built the agent that does the paying: a research agent with a $1 budget that discovers, evaluates, and buys content from competing publishers, settling on Base Sepolia.
Both posts looked at agentic commerce from the buyer’s seat. This one flips to the seller’s. If an agent will pay per article, what does the publisher have to build to charge it?
In Part 2 the answer was a Lambda@Edge function: about 40 lines that returned a 402 with a price and a wallet address. That function did one job well, establishing the protocol contract (here is the price, the network, the wallet), and it deliberately stopped there. It checked that a payment header was shaped right and waved the request through. It did not verify funds, and it did not settle anything on-chain. That was the right scope for a Part 2 whose subject was the agent, not the gate.
On 2026-06-15, AWS WAF shipped a feature that does the part the 40-line baseline left as an exercise: it verifies the signed payment authorization and settles on-chain, at the edge, as a managed action [1]. So I rebuilt the publisher on it and pointed the same agent at it. Here is the result first, then how it works.
What Launched: AI Traffic Monetization
AWS WAF AI traffic monetization lets a content or API provider charge AI bots and agents for access directly at the edge [1]. The mechanics, straight from the docs:
- A client requests a monetized resource.
- WAF returns an HTTP 402 Payment Required with pricing and the accepted payment networks.
- The client submits a signed payment authorization on its chosen network.
- WAF verifies the authorization, fetches the content from origin, and integrates with a third-party facilitator to settle the payment on-chain, then serves the response [1].
It speaks an open machine-to-machine payment protocol (x402), so any compatible client or agent runtime can complete the payment [1]. It is available globally with Amazon CloudFront at no additional charge beyond standard AWS WAF pricing [1].
The shape of the thing is two pieces of configuration on the web ACL:
- A
Monetizerule action. It is terminating: when a Monetize rule matches and the request carries no valid payment authorization, WAF returns the 402 directly and stops evaluating rules [2]. Alongside Monetize, a rule can useAllow,Block,Count,Captcha, orChallenge[2]. - A
MonetizationConfigholding the base price and the wallet that receives settlement. The base price is a decimal USD string up to three decimal places, minimum $0.001 USDC [2].
The lifecycle is worth drawing, because the interesting part is what the edge now does between the 402 and the 200:
The managed lifecycle: WAF returns the 402, verifies the signed authorization, fetches origin, and settles on-chain only on a 2xx. A failed origin is never charged.
Two details from the docs matter for anyone modelling the economics. Settlement happens after origin returns a successful response, so a failed origin (4xx/5xx) is not charged [4]. And AWS is not a party to, or in the flow of, funds: USDC transfers directly from the client’s wallet to your configured wallet via Coinbase Developer Platform’s x402 facilitator [4].
New Business Models for Publishers
A flat “every agent pays the same cent” paywall is the boring version. The managed feature is more interesting because it lets you price on two independent axes at once: what the content is and who is asking. The docs spell out the intent directly, lower prices for verified search crawlers that drive referral traffic, standard prices for known agents doing RAG or summarization, higher prices for unverified agents, and blocking training crawlers entirely or pricing them at a premium [2].
I encoded that as a two-dimensional matrix in a single web ACL. The effective price is the product of a content-tier multiplier and an agent-class multiplier:
effective_price = base_price x content_tier_multiplier x agent_class_multiplier
With a base of $0.002 USDC, that produces this grid:
| agent class \ content tier | /articles/ x1 | /data/ x3 | /premium/ x8 | WAF action |
|---|---|---|---|---|
| verified-crawler | free | free | free | Allow |
| known-agent (x1) | $0.002 | $0.006 | $0.016 | Monetize |
| unverified (x2) | $0.004 | $0.012 | $0.032 | Monetize |
| training | 403 | 403 | 403 | Block |
| human / no-agent | pass-through | pass-through | pass-through | default Allow |
One web ACL, every action the feature offers. Read that grid as a strategy, not a config. A search crawler that sends you readers reads for free. A known summarization agent pays the list price. An unverified bot pays double, because you cannot price its downstream behaviour. A training crawler gets a 403, because your terms forbid training. And a human never sees a 402 at all, because the Monetize action is designed for automated traffic and a browser cannot interpret the payment challenge [2].
Telling agents the price is not telling them the rules
Monetization answers how much. It does not answer what am I allowed to do with this once I have paid for it? For that, the feature leans on RSL (Really Simple Licensing), an open standard for machine-readable content licensing [3]. RSL terms can be discovered four ways: a directive in robots.txt, an HTTP Link header, an HTML <link rel="license"> element, or an RSS/Atom module [3].
I used the Link header path, injected by a CloudFront Response Headers Policy [3], pointing at a license document that permits read, inference, and search-indexing, and prohibits training, fine-tuning, and redistribution. The agent fetches that on a free path and decides whether the terms are acceptable before it ever pays.
There is one subtlety the docs are explicit about, and it bit my mental model until I read it carefully: the 402 challenge does not carry the Link header. CloudFront Response Headers Policies apply to responses served from origin, and the 402 is generated by WAF before origin is touched [3]. So an agent discovers your terms on a free fetch (the catalog, robots.txt, or license.xml), not on the payment challenge itself.
Implementation: What the Stack Actually Looks Like
The whole point of this publisher is what is missing from it: there is no Lambda@Edge. WAF returns the 402, verifies, fetches, and settles. The publisher owns configuration, not a function.
The catch when I built it: the Monetize action and MonetizationConfig are days old and not yet typed in the CDK CfnWebACL construct (and may not yet be in the CloudFormation schema). So I injected the whole rule set via the L1 escape-hatch. A small helper builds each priced rule:
// A Monetize rule whose PriceMultiplier already folds content-tier x agent-class.
const monetizeRule = (name, priority, statement, multiplier) => ({
Name: name,
Priority: priority,
Action: { Monetize: { PriceMultiplier: multiplier } }, // effective = base x multiplier
VisibilityConfig: vis(name),
Statement: statement,
});
Because Monetize is terminating, rule order encodes precedence. The class gates that do not price (Allow / Block / pass-through) are evaluated first; then the priced rules, unverified before standard, so the class multiplier composes with the content-tier multiplier:
webAcl.addPropertyOverride("Rules", [
// 0 outer demo IP gate (Block anything not in the allowlist)
// 1 Bot Control, label-only (Count) — classify without blocking
// 10 human -> Allow (pass-through, never a 402)
// 11 training -> Block (403)
// 12 verified-crawler -> Allow (free, referral-driving)
monetizeRule("UnverifiedPremium", 20, andStmt(klass("unverified"), tier("/premium/")), "16"),
monetizeRule("UnverifiedData", 21, andStmt(klass("unverified"), tier("/data/")), "6"),
monetizeRule("UnverifiedArticles",22, andStmt(klass("unverified"), tier("/articles/")),"2"),
// 30-32 standard (known-agent / unclassified bot) x content tier
monetizeRule("StandardPremium", 30, tier("/premium/"), "8"),
monetizeRule("StandardData", 31, tier("/data/"), "3"),
monetizeRule("StandardArticles", 32, tier("/articles/"),"1"),
]);
The base price and the payout wallet live once, in the MonetizationConfig (Test mode keeps the whole matrix exercisable on Base Sepolia at zero cost):
webAcl.addPropertyOverride("MonetizationConfig", {
CurrencyMode: "TEST", // flip to REAL for go-live
CryptoConfig: {
PaymentNetworks: [{
Chain: "BASE_SEPOLIA", // -> BASE / SOLANA for production
WalletAddress: "<YOUR_TESTNET_WALLET>",
Prices: [{ Amount: "0.002", Currency: "USDC" }],
}],
},
});
The RSL license pointer is a one-line Response Headers Policy on the distribution:
new cloudfront.ResponseHeadersPolicy(this, "RslLinkHeaderPolicy", {
customHeadersBehavior: { customHeaders: [{
header: "Link",
value: `<${licenseUrl}>; rel="license"; type="application/rsl+xml"`,
override: true,
}]},
});
And the part I cared most about: the same agent from Part 2 pays this publisher with no changes. The AgentCore Payments SDK (bedrock-agentcore 1.14.1) reads WAF’s payment-required response header, parses the x402 v2 challenge, signs an authorization, and emits a PAYMENT-SIGNATURE request header on the retry, which is exactly what WAF’s verifier expects. No mapping shim, no CloudFront Function, no client-side glue. The buy side and the sell side met in the middle on a protocol, which is the whole promise of agentic commerce.
Simulation versus production, stated plainly
One honest caveat. In my demo the agent class is read from a self-asserted request header (x-demo-agent-class: known-agent | unverified | training | verified-crawler | human). That is a simulation so a viewer can drive the entire matrix from curl with no extra infrastructure, and a self-asserted header is trivially spoofable, so never price real traffic on it.
In production you replace each header match with a LabelMatchStatement on AWS WAF Bot Control labels (awswaf:managed:aws:bot-control:bot and verified-bot sub-labels), plus Web Bot Auth signatures for the known/verified distinction. The docs recommend exactly this, combining Monetize with Bot Control so only bot traffic ever receives the 402 and human users pass through untouched [2]. There is also a neat inversion worth noting: in production no label means human means pass-through, whereas my simulation defaults no header means standard monetize, because the demo’s subject is the paying agent.
What I Actually Verified
Because this feature was two days old when I built on it, I did not want to take the happy path on faith. Running it live in a test account on Base Sepolia testnet, here is what held up and what to watch.
CloudFormation accepted the new shape. The deploy applied the Monetize action, all six PriceMultiplier values, and the MonetizationConfig with no property rejection and no rollback, and the synthesized template carried every multiplier. In my testing the escape-hatch path was enough; I did not need the documented post-deploy CLI fallback.
The matrix then differentiated exactly as designed. Curling the same paths as each simulated class produced distinct prices, Allows, and 403s. Decoding the base64 x402 challenge confirmed the numbers down to the micro-unit: premium for a known agent returned $0.016, the same path for an unverified agent returned $0.032 (the x2 class multiplier), a training crawler got a 403, and a verified crawler got the content free.
Then it settled for real. With a funded, granted wallet, the agent bought three articles across all three tiers and settled each on-chain. The amounts matched the matrix exactly, $0.002 + $0.006 + $0.016 = $0.024 total, and the payer’s USDC balance dropped from 20.000 to 19.976, confirmed against the chain. Each purchase returned a payment-response header carrying its transaction hash. Those are public testnet transactions; the three hashes are in the repo’s validation notes for anyone who wants to verify them on a block explorer.
Two caveats I would want to know as a reader. First, the installed AWS CLI could not read back the new fields, aws wafv2 get-web-acl rendered the Monetize action as an empty object, because the client model predated the feature. The live 402 behaviour is authoritative; trust the decoded challenge, not a stale CLI read, and upgrade the CLI to introspect the config. Second, getting from “deployed” to “settled” was a three-gate exercise: the embedded-wallet instrument needs a linked account, the Coinbase project needs delegated signing enabled, and the specific wallet needs a one-time WalletHub grant. None of that is WAF; it is the wallet side, and the repo documents it as a runbook so you do not lose an afternoon to opaque errors like I did.
A last operational note from the docs, not my opinion: paid requests add several seconds of latency for verification and on-chain settlement, and only those requests, signature-less and non-monetized traffic is unaffected [2]. High payment volume may be throttled, so clients should retry with a brief backoff [2].
The Economics, in One Synthetic Paragraph
The demo’s premium article is a fictional economist’s TCO model of three paywall approaches, and it makes the argument better than I can paraphrase it: a structural-only check leaks revenue on plausible-but-fake proofs; bolting on your own verification and settlement closes the leak but adds a settlement service, key management, replay protection, and on-call; and the managed edge action collapses that engineering to configuration while replay protection and idempotency come from the protocol. The real trade is bespoke control for a fixed, audited protocol, which is usually the right trade when your moat is your content, not your payment plumbing. It is synthetic content written for the demo, so take the numbers as illustrative, but the direction of the argument is why this feature is interesting to a publisher.
If You’re Running This on AWS
The publisher side of this demo is, end to end:
| Service | Role | Why |
|---|---|---|
| AWS WAF (AI traffic monetization) | 402 + verify + settle at the edge | Managed action; no edge function to own [1] |
| Amazon CloudFront | Content delivery + RSL Link header | Response Headers Policy injects license discovery [3] |
| Amazon S3 | Origin content | Static tiers behind the distribution |
| Coinbase CDP x402 facilitator | On-chain settlement | AWS is not in the flow of funds [4] |
| AWS WAF Bot Control | Agent-class classification (production) | Label-match instead of a self-asserted header [2] |
For the buyer side (the agent, AgentCore Payments, the wallet model), Part 2 walks through it in depth. The two sides share exactly one thing, the x402 protocol on the wire, and that is what lets the Part 2 agent pay this Part 3 publisher unchanged.
The Code
The full source for both publishers (the original Lambda paywall and this managed WAF publisher) and the paying agent is on GitHub:
github.com/stechr/agentcore-payments-media-demo
It deploys with CDK. You can stand up the old publisher, the new WAF publisher, or both, and point the same agent at either. The matrix (Allow / Monetize / Block plus the 402 prices) is fully observable with curl at zero cost on testnet; only the actual on-chain settlement needs your own funded and granted wallet, which the README documents as the one foreground prerequisite.
Where This Leaves the Series
Three posts in, the picture is symmetrical. The protocol layer (x402) and the buyer’s infrastructure (AgentCore Payments) already work, that was Part 2. Now the seller’s infrastructure works too, as a managed edge feature you configure rather than code. What is still unsolved is the layer in the middle that Part 2 ended on: trust. A publisher can now price by agent class, but classifying that agent honestly, and an agent deciding which priced publisher to believe, is the hard problem neither a Lambda nor a managed action solves for you.
The plumbing is done. The judgment is not. That is the interesting part, and where I think the next moat gets built.
If you are a publisher weighing this, or building on the agent side, I would like to compare notes. Would you let a verified search crawler read for free to keep the referral traffic, or is every agent a paying customer now?
This is Part 3 of the Agentic Commerce series. Part 1: HTTP 402: The 30-Year Placeholder That AI Agents Finally Activated. Part 2: I Built the Agent That Pays.
Sources
[1] AI traffic monetization — AWS WAF Developer Guide. https://docs.aws.amazon.com/waf/latest/developerguide/waf-ai-traffic-monetization.html
[2] Pricing configuration (base price, PriceMultiplier, Monetize as a terminating action, Bot Control labels, latency, throttling) — AWS WAF Developer Guide. https://docs.aws.amazon.com/waf/latest/developerguide/waf-ai-traffic-monetization-pricing.html
[3] Communicating license terms to AI agents (RSL, the CloudFront Link header, and why the 402 does not carry it) — AWS WAF Developer Guide. https://docs.aws.amazon.com/waf/latest/developerguide/waf-ai-traffic-monetization-license-terms.html
[4] Payment networks and settlement (AWS not in the flow of funds, verify-before-fetch, settle-on-2xx, Coinbase x402 facilitator) — AWS WAF Developer Guide. https://docs.aws.amazon.com/waf/latest/developerguide/waf-ai-traffic-monetization-payment.html
[5] HTTP 402: The 30-Year Placeholder That AI Agents Finally Activated (Part 1). https://schristoph.online/blog/http-402-agents-pay/
[6] I Built the Agent That Pays (Part 2). https://schristoph.online/blog/building-agent-that-pays/
[7] agentcore-payments-media-demo — full source (both publishers + the paying agent). https://github.com/stechr/agentcore-payments-media-demo
#AgenticCommerce #AWSWAF #HTTP402 #x402 #AIAgents
About the Author
Stefan Christoph is a Principal Solutions Architect at AWS, focused on agentic AI, media & entertainment, and helping builders move from demo to production. He writes about AI architecture, developer productivity, and the future of software.
This is a personal blog. Opinions expressed here are my own and do not represent the views or positions of my employer.
🎬 Also available as a blog walkthrough video on YouTube
❤️ Created with the support of AI (Kiro)