Как готовить коммерцию без витрины к эпохе агентных языковых моделей
Покупатели становятся агентами. Что нужно, чтобы языковая модель могла прочитать предложение, подписать его и оплатить без человека, и что Hanzo уже отдаёт для этого сегодня.

На этой странице
Headless-коммерция отделила витрину от движка, чтобы любой фронтенд мог продавать через один API. Этот фронтенд всегда был тем, на что смотрит человек: сайт, приложение, киоск.
Следующий фронтенд не смотрит. Он читает. Покупатель становится агентом: программой с целью, бюджетом и циклом инструментов, которую отправили найти тариф, GPU или API платежей. Он покупает у того, кого может понять и кому может заплатить, не прося человека о помощи. Если вашему бизнесу нужен человек, чтобы прочитать страницу, закрыть баннер cookie, открыть письмо с подтверждением или ввести номер карты, агент берёт следующий результат.
Вот тезис. Бизнес должен быть читаемым и транзакционным для LLM без человека в контуре. Читаемый — значит, что агент может узнать, что вы продаёте, сколько это стоит и как начать. Транзакционный — значит, что он может начать, заплатить и получить вещь через API, сам.
Вот что для этого нужно, пункт за пунктом, и что Hanzo отдаёт уже сегодня. Каждый адрес ниже отвечает без учётной записи, так что вы можете проверить каждое утверждение сами.
Отдавать каждую страницу как Markdown
Агент платит за каждый токен, который читает. На hanzo.ai добавьте .md к адресу страницы, и вы получите страницу как Markdown: hanzo.ai/pricing.md, hanzo.ai/about.md. Каждая страница называет свою пару в заголовке через <link rel="alternate" type="text/markdown">, чтобы агенту не приходилось гадать.
26 октября я запросил нашу страницу агентов обоими способами: 528,810 байт как HTML, 17,465 как Markdown. Затем я взял каждый двенадцатый URL в нашем sitemap, 70 из 829, и запросил его пару. Все 70 вернулись 200 как text/markdown.
Опубликовать указатель, с которого агент может начать
Агенту, который о вас никогда не слышал, нужен один адрес, ведущий всюду. Мы публикуем три.
- hanzo.ai/llms.txt — текстовая карта сайта. Она говорит, что такое Hanzo, затем даёт раздел "For agents" с точными шагами, как получить ключ, и потом перечисляет страницы, по одной строке. docs.hanzo.ai/llms.txt делает то же для документации.
- api.hanzo.ai/v1 перечисляет каждую возможность, которую отдаёт API, ключ не нужен, а api.hanzo.ai/v1/openapi.json содержит каждую операцию с её схемой. Для продаж в указателе есть возможность
commerce: оплата, подписки, счета, вебхуки платежей и каталог витрины. - hanzo.ai/.well-known/api-catalog — стандартное место (RFC 9727), куда клиент смотрит первым. Оно указывает на документ OpenAPI, документацию и страницу статуса.
Принять краулеров сознательно
robots.txt — первый файл, который запрашивает любой краулер. Наш называет двенадцать ИИ-краулеров по одному, среди них GPTBot, ClaudeBot, Claude-User, PerplexityBot и Google-Extended, и разрешает их. Они получают те же исключения, что и все остальные: вход, учётная запись, оплата и пути API.
Блокировать ИИ-краулеров — законный выбор. Блокировать их потому, что так сказал параметр по умолчанию, — это вообще не выбор.
Сделать цену точкой данных
Агенты сравнивают. Цену внутри картинки или за кнопкой "contact us" сравнить нельзя. api.hanzo.ai/v1/billing/plans не требует ключа и возвращает JSON: Free по 0, Pro по 2000 в месяц, Team по 2500 за место, и рядом пределы каждого тарифа. Суммы в центах.
Дать кошельку зарегистрироваться
У агента нет почты, нет телефона и нет карты. У него есть пара ключей. Поэтому hanzo.id впускает кошелёк: запросите одноразовый nonce для адреса кошелька, подпишите сообщение, которое он описывает, отправьте подпись и обменяйте вернувшийся код на токен.
Кошелёк, которого Hanzo ещё не видел, получает собственную учётную запись и собственную организацию, кошелёк становится администратором, на тарифе Free. Токен живёт семь дней. Никто не открывает письмо и не подтверждает устройство.
Один вызов для ключа и небольшой запас, чтобы попробовать
С токеном POST /v1/account/keys возвращает ключ sk-live-, который живёт дольше токена. Сохраните его; документация говорит, что его показывают один раз.
Тариф Free — это 50 запросов в день на zen-free, без карты, и каждый ответ сообщает остаток в x-ratelimit-remaining. Мой первый вызов ответил 200, оставалось 49.
Принять платёж без человека: стейблкоины через API
POST /v1/billing/crypto/deposit принимает сеть, токен и сумму и возвращает адрес депозита, который действует 24 часа. GET /v1/billing/crypto/options перечисляет, что он принимает: сети Base, Ethereum и Solana и токены USDC и USDT.
Я остановился на адресе и не отправил денег. То, что баланс зачисляется, когда сеть подтверждает перевод, написано в нашей документации; сам я это не прогонял. Тарифы и подписки остаются оплатой картой в браузере. Агенту от нас нужен баланс, а у баланса есть API.
Весь поток в одном файле
Это hanzo.ai/agents#headless, сокращённо. Каждая строка такая, как её печатает та страница; я убрал второй комментарий в шапке, вызов опций и строку, которая печатает префикс ключа. Нужны Node 22.18 или новее и npm i viem, затем node headless.mts. Я прогнал этот блок на продакшене со свежим кошельком: токен, ключ, бесплатный вызов и адрес депозита USDC.
// No human required: sign in with a wallet, get a key, call the API, top up in USDC.
// npm i viem · node headless.mts (Node 22.18+; set HANZO_WALLET_KEY to use your own wallet)
import { createHash, randomBytes } from 'node:crypto'
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
const ID = 'https://hanzo.id'
const API = 'https://api.hanzo.ai'
const CLIENT = 'hanzo-ai'
const REDIRECT = 'http://localhost:3000/callback'
const account = privateKeyToAccount((process.env.HANZO_WALLET_KEY as `0x${string}`) ?? generatePrivateKey())
const json = async (r: Response) => {
const body = await r.json()
if (!r.ok || body?.status === 'error') throw new Error(`${r.url} ${r.status} ${JSON.stringify(body)}`)
return body
}
// 1. A one-time challenge for this wallet.
const { data: c } = await json(await fetch(`${ID}/v1/iam/web3/nonce?chain=evm&address=${account.address}`))
// 2. The CAIP-122 message the challenge describes, signed with EIP-191 personal_sign.
const message = [
`${c.domain} wants you to sign in with your Ethereum account:`,
account.address,
'',
...(c.statement ? [c.statement, ''] : []),
`URI: ${c.uri}`,
`Version: ${c.version}`,
`Nonce: ${c.nonce}`,
`Issued At: ${c.issuedAt}`,
...(c.expirationTime ? [`Expiration Time: ${c.expirationTime}`] : []),
].join('\n')
const signature = await account.signMessage({ message })
// 3. Verify: a first-seen wallet gets an account and an organization of its own.
const verifier = randomBytes(32).toString('base64url')
const challenge = createHash('sha256').update(verifier).digest('base64url')
const { data: code } = await json(
await fetch(`${ID}/v1/iam/web3/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: CLIENT, chain: 'evm', scheme: 'secp256k1-eip191', address: account.address, message, signature,
type: 'code', redirectUri: REDIRECT, scope: 'openid profile email', codeChallenge: challenge, codeChallengeMethod: 'S256',
}),
}),
)
// 4. The authorization code for a token.
const token = await json(
await fetch(`${ID}/v1/iam/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: 'authorization_code', client_id: CLIENT, code, code_verifier: verifier, redirect_uri: REDIRECT }),
}),
)
const bearer = { Authorization: `Bearer ${token.access_token}`, 'Content-Type': 'application/json' }
// 5. A key that outlives the token. Shown once: keep it in your secret store.
const minted = await json(await fetch(`${API}/v1/account/keys`, { method: 'POST', headers: bearer, body: JSON.stringify({ name: 'agent', type: 'secret' }) }))
const key = { Authorization: `Bearer ${minted.key}`, 'Content-Type': 'application/json' }
// 6. A first call, free.
const chat = await fetch(`${API}/v1/chat/completions`, {
method: 'POST', headers: key,
body: JSON.stringify({ model: 'zen-free', messages: [{ role: 'user', content: 'Reply with the single word: ready' }] }),
})
console.log('chat', chat.status, chat.headers.get('x-hanzo-served'), 'free left today:', chat.headers.get('x-ratelimit-remaining'))
console.log((await chat.json()).choices?.[0]?.message?.content)
// 7. Top up in USDC: ask for a deposit address, then send to exactly that address.
console.log(await json(await fetch(`${API}/v1/billing/crypto/deposit`, { method: 'POST', headers: key, body: JSON.stringify({ chain: 'base', token: 'usdc', amountCents: 500 }) })))
x402 на подходе, и его ещё нет
Дальше пополнения правильная форма для агента — плата за запрос. Сервер отвечает 402 Payment Required с ценой, клиент платит и повторяет. Это x402. На нашем API этого нет: ни один эндпоинт Hanzo пока не называет цену таким образом, и я не стану это утверждать. До тех пор агент платит нам балансом, пополненным через API.
Почему важны нулевые ошибки краулера
Ничто из этого не падает громко. Агент, который идёт по ссылке из вашего sitemap в 404, не заводит тикет. Он берёт следующий результат. Относитесь к ошибке краулера так, как магазин относится к сломанной кассе.
Я запросил те же 70 страниц выборки со строкой user-agent ClaudeBot и ещё раз со строкой Googlebot. Каждая ответила 200 оба раза. Это выборка, не гарантия, а строка user-agent — не собственный адрес краулера, поэтому я не могу показать вам, что видят настоящие краулеры. Сделайте то же на своём сайте: запросите sitemap теми агентами, которых вы приглашаете, и посчитайте ответы, которые не 200.
Мы здесь уже были
Я основал эту компанию в 2014 году, как Crowdstart; в 2016 году мы переименовали её в Hanzo. Наш первый клиент, SKULLY, собрал $1 million на Indiegogo примерно за 45 часов, и TechCrunch назвал это самой быстрой полностью профинансированной кампанией на Indiegogo. По моему счёту Hanzo с самого начала был коммерцией, рождённой для ИИ: сначала собственная аналитика, затем модели, которые учились на данных компаний на платформе. В 2017 году мы вошли в Techstars (заметка того июня), и тем, что мы, по нашим словам, строили, была разумная коммерция.
В 2020 Damon Motorcycles вышел на CES, и кампанию вёл Hanzo. За 30 дней платформа продала мотоциклов на $8.4M при рекламных расходах $15,532, отдача 541:1. Аудитории пересобирались из наших клиентских данных каждые 12 часов, а написанные ИИ группы объявлений получили на Facebook оценку Above Average менее чем за 36 часов. Остальное есть в разборе кейса.
Это были машины на стороне продажи: они находили аудитории, которые покупали, и писали объявления. Дальше сторона покупки. Тот же цикл вот-вот пойдёт с другого конца, и ваш бизнес будет прочитан и оплачен машиной либо пропущен ею.
Направьте своего агента на hanzo.ai/agents.
Читать далее

Hanzo Payments: Unified Payment Processing for Global Commerce
Launching Hanzo Payments — unified payment processing with intelligent routing, fraud detection, and multi-currency support.

Introducing the Hanzo Platform
Crowdstart evolves into Hanzo: a complete commerce platform for the modern era.

Маршрутизатор и есть реестр
В Hanzo Cloud нет зафиксированного файла OpenAPI. Живая таблица маршрутов — источник, спроецированный тремя способами, и тест биекции роняет сборку, если любая проекция расходится. 1,362 операции на 963 путях, порождаются на процесс в момент запроса.
