How to Add Surf to an E-Commerce Site
Let AI agents search your catalog, manage carts, and complete checkouts β all through typed commands instead of brittle UI scraping.
The Problem
Today, when an AI agent tries to buy something from a webshop, it follows a painful process: screenshot the page, use a vision model to find the βAdd to Cartβ button, simulate a click, wait for the page to reload, screenshot again, and repeat. Each step takes seconds, costs money (vision model inference), and frequently fails when UI elements shift or load dynamically.
For a typical checkout flow β search β select product β add to cart β fill shipping β pay β that's 15-20 screenshots, ~$1.00 in vision costs, and a success rate around 60%. Not good enough for production use.
Before Surf
With Surf
The Solution: Surf Commands
With Surf, you expose your store's core actions as typed commands. Agents discover them via surf.json and execute them directly β no screenshots, no guessing.
Step 1: Define Your Commands
Start with the core e-commerce actions: search, product details, cart management, and checkout.
import { createSurf } from '@surfjs/core'Β const surf = createSurf({ name: 'My Store', commands: { // Search products search: { description: 'Search the product catalog', params: { query: { type: 'string', required: true }, category: { type: 'string' }, limit: { type: 'number', default: 20 }, sort: { type: 'string', enum: ['price', 'rating', 'newest'] }, }, hints: { idempotent: true, sideEffects: false }, run: async ({ query, category, limit, sort }) => { const products = await db.products.search({ query, category, limit, sort }) return { results: products, total: products.length } }, },Β // Get product details 'product.get': { description: 'Get detailed product information', params: { id: { type: 'string', required: true } }, hints: { idempotent: true, sideEffects: false }, run: async ({ id }) => db.products.findById(id), },Β // Cart namespace cart: { add: { description: 'Add an item to the shopping cart', auth: 'required', params: { sku: { type: 'string', required: true }, quantity: { type: 'number', default: 1 }, }, run: async ({ sku, quantity }, ctx) => { const cart = (ctx.state?.cart as string[]) ?? [] cart.push(...Array(quantity).fill(sku)) return { cart, added: sku, quantity } }, }, view: { description: 'View current cart contents', auth: 'required', run: async (_, ctx) => ({ items: ctx.state?.cart ?? [] }), }, },Β // Checkout checkout: { description: 'Complete the purchase', auth: 'required', params: { shipping: { type: 'object', required: true, properties: { address: { type: 'string', required: true }, city: { type: 'string', required: true }, zip: { type: 'string', required: true }, }, }, paymentToken: { type: 'string', required: true }, }, run: async ({ shipping, paymentToken }, ctx) => { const order = await orders.create({ items: ctx.state?.cart ?? [], shipping, paymentToken, }) return { orderId: order.id, status: 'confirmed' } }, }, },})Step 2: Mount the Middleware
Add Surf to your Express (or Next.js, Fastify, Hono) server with a single line:
import express from 'express'import { surf } from './surf-commands'Β const app = express()app.use(express.json())app.use(surf.middleware())// That's it. Surf now serves:// GET /.well-known/surf.json (manifest with ETag/304)// POST /surf/execute (command execution)// POST /surf/pipeline (multi-step pipelines)// POST /surf/session/start (start a session)// POST /surf/session/end (end a session)Β app.listen(3000)Step 3: Agent-Side Experience
Now any AI agent can complete a full checkout in milliseconds:
import { SurfClient } from '@surfjs/client'Β const client = await SurfClient.discover('https://mystore.com')Β // Full checkout pipeline in one requestconst response = await client.pipeline([ { command: 'search', params: { query: 'wireless headphones' } }, { command: 'cart.add', params: { sku: '$prev.results[0].sku' } }, { command: 'checkout', params: { shipping: { address: '123 Main St', city: 'Copenhagen', zip: '2100' }, paymentToken: 'tok_...' }},], { sessionId: 'sess_abc' })Β console.log(response.results[2].result)// => { orderId: 'ord_abc123', status: 'confirmed' }Adding Rate Limiting
Protect your checkout endpoint from abuse with per-command rate limits:
// In your createSurf config:checkout: { description: 'Complete the purchase', auth: 'required', rateLimit: { windowMs: 3600_000, // 1 hour maxRequests: 10, // 10 checkouts per hour keyBy: 'auth', // Rate limit per authenticated user }, run: async ({ shipping, paymentToken }, ctx) => { // ... },}Summary
By adding Surf to your e-commerce site, you transform it from an opaque, screenshot-dependent experience into a clean, typed API that any AI agent can use reliably. The investment is minimal β a few command definitions β and the payoff is immediate: faster transactions, zero vision costs, and near-perfect reliability.
Ask your agent
Copy these prompts into Claude, OpenClaw, or any AI agent
Add Surf to my Express store. Install @surfjs/core, then define commands in createSurf(): search (query: string), cart.add (sku: string, qty: number), cart.view, checkout (address: string). Mount surf.middleware() on the Express app. Make sure /.well-known/surf.json returns the manifest.
I want to make my Next.js e-commerce site agent-navigable using Surf.js. Install @surfjs/core, create a /app/api/surf/route.ts with createSurf() and commands for searching products, adding to cart, and checking out. Add a rewrite in next.config.js so /.well-known/surf.json points to the API route.
Use @surfjs/client to discover http://localhost:3000, list all available commands, then run a pipeline: search for "laptop" β add the first result to cart β get cart summary. Print the result.
π‘ Works with OpenClaw, Claude Code, Cursor, Codex, and any agent that can make HTTP requests.