Turn a website flow into a paid API. Declare it in YAML, Puppeteer executes it, and the extracted data, screenshots and step log come back in the response.
npm install x402-browser-bridge
⚠️ Terms of service. Automating a website you do not own or have permission to automate may breach its terms of service, and in some jurisdictions may break the law. This is a tool for driving browsers; it cannot know whether you are allowed to drive a particular one.
Before pointing a flow at a site that isn't yours: read its terms, check robots.txt, never automate an authenticated session you don't own, don't circumvent CAPTCHAs or rate limits, and treat extracted personal data as regulated data. Reselling a paid bridge over someone else's site turns a terms problem into a commercial one — with you as the party charging.
Every flow bundled with this repository targets a page the repository serves itself. No third-party site is automated by anything shipped here. That is the model to copy. If you deploy it, you are the operator, and the legality of every flow you serve is yours.
An enormous amount of commerce still lives behind a form. No API, no partner programme, no webhook — just a page, a submit button, and a confirmation screen. An agent that can pay for things cannot use any of it.
A bridge closes that gap: declare the click path once, expose it as an x402 endpoint, and the agent buys the outcome without ever knowing a browser was involved.
| Route | Price | What you get back |
|---|---|---|
POST /run/:flow | $0.05 | Signed { extracted, screenshots, steps } in the 200 body |
GET /flows | free | Every declared flow: inputs, extracted fields, price, host allowlist |
POST /verify | free | Signature check on a run record |
GET /health | free | Liveness, rails, flow count, browser availability |
Prices come from the flow files, so adding a YAML file adds a priced route — no code change.
name: demo-booking
price: "$0.05"
allowHosts: [localhost, 127.0.0.1] # the security boundary. Required.
inputs:
name: { type: string, required: true }
email: { type: string, required: true, pattern: '[^@\s]+@[^@\s]+\.[^@\s]+' }
date: { type: string, required: true, pattern: '\d{4}-\d{2}-\d{2}' }
steps:
- goto: "{{baseUrl}}/demo/booking.html"
- type: { selector: "#name", value: "{{inputs.name}}" }
- type: { selector: "#email", value: "{{inputs.email}}" }
- select: { selector: "#party", value: "{{inputs.partySize}}" }
- screenshot: form
- click: "#submit"
- waitFor: "#confirmation:not([hidden])"
timeoutMs: 8000
- screenshot: confirmation
extract:
reference: { selector: "#reference" }
errorMessage: { selector: "#error:not([hidden])", optional: true }
Steps: goto · click · type · select · press · waitFor · waitForText · wait · scroll · screenshot. A flow cannot execute arbitrary JavaScript — templating is {{inputs.x}} and {{baseUrl}}, nothing more. Adding a flow is a config change with a reviewable diff.
A failed run is still a 200: the payment settled before the browser started, so a bare 500 would take the money and teach the caller nothing. Instead you get the step that broke, a screenshot of the page at that moment, and whatever the site said — which is usually the actual answer ("no tables available"), not a bug.
What fails free, before payment: unknown flow (404), malformed inputs (400), and no browser available (503).
No eval, no script injection, no dynamic selectors. YAML in, clicks out.
allowHosts is required. Off-site navigation is aborted at the request-interception layer, so even a redirect can't escape.
Whole-run timeout (60s default, 180s cap), per-step timeouts, max 60 steps, 128 KB body limit.
| Rail | Network | payTo | Facilitator |
|---|---|---|---|
| EVM | base-sepolia / base | 0x40252CFDF8B20Ed757D61ff157719F33Ec332402 | x402.org |
| Solana | solana / solana-devnet | WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW | PayAI |
Run it, pay a 402, execute a flow, then write your own.
Every route, every step type, the full flow schema.
Discovery, both rails, screenshots into vision models, MCP.
The agent-facing contract in one file.
Machine-readable manifest for x402 indexes.
Including the 402 response and PaymentRequirements schema.