Home/Guides/E-commerce
Guide

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

βœ— 15-20 screenshots per checkout
βœ— ~$1.00 in vision model costs
βœ— ~60% success rate
βœ— 2-3 minutes per transaction

With Surf

βœ“ 3-4 API calls per checkout
βœ“ $0.00 in vision costs
βœ“ ~100% success rate
βœ“ Under 200ms total

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.

surf-commands.ts
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:

server.ts
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:

agent.ts
import { SurfClient } from '@surfjs/client'
Β 
const client = await SurfClient.discover('https://mystore.com')
Β 
// Full checkout pipeline in one request
const 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:

typescript
// 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

OpenClaw / Claude

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.

Claude Code / Cursor

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.

Test with @surfjs/client

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.