Comércio headless na era dos LLMs que compram sozinhos
Os compradores estão virando agentes. O que é preciso para um LLM conseguir ler, assinar e pagar sem uma pessoa no meio, e o que Hanzo entrega hoje para isso.

Nesta página
O comércio headless separou a vitrine do motor, para que qualquer frente pudesse vender por uma única API. Essa frente sempre foi algo que uma pessoa olhava: um site, um app, um quiosque.
A próxima frente não olha. Ela lê. O comprador está virando um agente: um programa com um objetivo, um orçamento e um ciclo de ferramentas, enviado para achar um plano, uma GPU ou uma API de pagamentos. Ele compra de quem consegue entender e pagar sem pedir ajuda a uma pessoa. Se o seu negócio precisa que alguém leia uma página, feche um aviso de cookies, abra um e-mail de confirmação ou digite um número de cartão, o agente fica com o próximo resultado.
Essa é a tese. Um negócio precisa ser legível e transacionável por um LLM sem nenhuma pessoa no circuito. Legível quer dizer que o agente consegue aprender o que você vende, quanto custa e como começar. Transacionável quer dizer que ele consegue começar, pagar e receber a coisa por uma API, sozinho.
Eis o que isso exige, ponto por ponto, junto com o que Hanzo entrega hoje. Todo endereço abaixo responde sem conta, então você pode conferir cada afirmação por conta própria.
Servir cada página como Markdown
Um agente paga por cada token que lê. Em hanzo.ai, acrescente .md ao endereço de uma página e você recebe a página como Markdown: hanzo.ai/pricing.md, hanzo.ai/about.md. Cada página nomeia a sua gêmea no cabeçalho com <link rel="alternate" type="text/markdown">, para o agente não precisar adivinhar.
Em 26 de outubro eu busquei a nossa página de agentes das duas formas: 528,810 bytes como HTML, 17,465 como Markdown. Depois peguei uma URL a cada doze no nosso sitemap, 70 de 829, e pedi a gêmea. Todos os 70 voltaram 200 como text/markdown.
Publicar um índice de onde um agente possa partir
Um agente que nunca ouviu falar de você precisa de um endereço que leve a todo lugar. Publicamos três.
- hanzo.ai/llms.txt é um mapa do site em texto puro. Ele diz o que é Hanzo, depois traz uma seção "For agents" com os passos exatos para obter uma chave, e então lista as páginas, uma linha cada. docs.hanzo.ai/llms.txt faz o mesmo para a documentação.
- api.hanzo.ai/v1 lista cada capacidade que a API serve, sem chave, e api.hanzo.ai/v1/openapi.json tem cada operação com o seu esquema. Para vender, o índice inclui uma capacidade
commerce: checkout, assinaturas, faturas, webhooks de pagamento e o catálogo da vitrine. - hanzo.ai/.well-known/api-catalog é o lugar padrão (RFC 9727) onde um cliente olha primeiro. Ele aponta para o documento OpenAPI, a documentação e a página de status.
Receber os crawlers de propósito
robots.txt é o primeiro arquivo que qualquer crawler pede. O nosso nomeia doze crawlers de IA um por um, entre eles GPTBot, ClaudeBot, Claude-User, PerplexityBot e Google-Extended, e os permite. Eles recebem as mesmas exceções que todo mundo: login, conta, pagamento e os caminhos da API.
Bloquear crawlers de IA é uma escolha legítima. Bloqueá-los porque um padrão dizia isso não é escolha nenhuma.
Fazer do preço um dado
Agentes comparam. Um preço dentro de uma imagem, ou atrás de um botão "contact us", não dá para comparar. api.hanzo.ai/v1/billing/plans não precisa de chave e devolve JSON: Free a 0, Pro a 2000 por mês, Team a 2500 por assento, com os limites de cada plano ao lado. Os valores estão em centavos.
Deixar uma carteira se cadastrar
Um agente não tem caixa de entrada, nem celular, nem cartão. Tem um par de chaves. Por isso hanzo.id cadastra uma carteira: peça um nonce de uso único para o endereço da carteira, assine a mensagem que ele descreve, envie a assinatura e troque o código que volta por um token.
Uma carteira que Hanzo ainda não tinha visto ganha a própria conta e a própria organização, com a carteira como admin, no plano Free. O token dura sete dias. Ninguém abre um e-mail nem aprova um aparelho.
Uma chamada para uma chave, e uma cota pequena para testar
Com o token, POST /v1/account/keys devolve uma chave sk-live- que dura mais que ele. Guarde-a; a documentação diz que ela aparece uma vez só.
O plano Free é de 50 requisições por dia em zen-free, sem cartão, e cada resposta diz o que resta em x-ratelimit-remaining. A minha primeira chamada respondeu 200 com 49 restantes.
Receber pagamento sem uma pessoa: stablecoins pela API
POST /v1/billing/crypto/deposit recebe uma chain, um token e um valor, e devolve um endereço de depósito válido por 24 horas. GET /v1/billing/crypto/options lista o que aceita: as chains Base, Ethereum e Solana, e os tokens USDC e USDT.
Parei no endereço e não enviei dinheiro. Que o saldo é creditado quando a chain confirma é o que a nossa documentação diz, não algo que eu tenha executado. Planos e assinaturas continuam sendo checkout com cartão no navegador. O que um agente precisa de nós é o saldo, e o saldo tem uma API.
O fluxo inteiro, em um arquivo só
Isto é hanzo.ai/agents#headless, abreviado. Cada linha está como aquela página a imprime; deixei de fora o segundo comentário de cabeçalho, a chamada de opções e a linha que imprime um prefixo de chave. Você precisa de Node 22.18 ou mais novo e npm i viem, depois node headless.mts. Rodei este bloco contra a produção com uma carteira nova: um token, uma chave, uma chamada gratuita e um endereço de depósito em 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 está chegando, e ainda não está aqui
Depois de uma recarga, a forma certa para um agente é pagar por requisição. O servidor responde 402 Payment Required com um preço, o cliente paga e tenta de novo. Isso é o x402. Não está no ar na nossa API: nenhum endpoint de Hanzo informa um preço desse jeito ainda, então não vou afirmar isso. Até lá, um saldo recarregado pela API é como um agente nos paga.
Por que zero erros de crawler importam
Nada disso falha fazendo barulho. Um agente que segue um link do seu sitemap para um 404 não abre um ticket. Ele fica com o próximo resultado. Trate um erro de crawler como uma loja trata um checkout quebrado.
Busquei aquelas mesmas 70 páginas da amostra com a string user-agent do ClaudeBot e de novo com a do Googlebot. Todas responderam 200 as duas vezes. É uma amostra, não uma garantia, e uma string user-agent não é o endereço próprio do crawler, então não consigo te mostrar o que os crawlers de verdade veem. Faça o mesmo no seu site: busque o seu sitemap com os agentes que você está convidando e conte as respostas que não são 200.
A gente já passou por aqui
Comecei esta empresa em 2014, como Crowdstart; renomeamos para Hanzo em 2016. Nosso primeiro cliente, SKULLY, levantou $1 million no Indiegogo em cerca de 45 horas, o que a TechCrunch chamou de a campanha do Indiegogo totalmente financiada mais rápida. Do meu ponto de vista, Hanzo foi comércio nativo de IA desde o início: primeiro a nossa própria analítica, depois modelos que aprendiam com os dados das empresas na plataforma. Em 2017 entramos na Techstars (o post daquele junho), e o que dizíamos estar construindo era comércio inteligente.
Em 2020 a Damon Motorcycles lançou na CES com Hanzo rodando a campanha. Em 30 dias a plataforma vendeu $8.4M em motos com $15,532 de gasto em anúncios, um retorno de 541:1. Os públicos eram reconstruídos a partir dos nossos dados de clientes a cada 12 horas, e conjuntos de anúncios escritos por IA ficaram como Above Average no Facebook em até 36 horas. O estudo de caso tem o resto.
Aquilo eram máquinas do lado da venda, achando os públicos que compravam e escrevendo os anúncios. O lado da compra é o próximo. O mesmo ciclo está prestes a rodar a partir da outra ponta, e o seu negócio vai ser lido e pago por uma máquina, ou deixado para trás por uma.
Aponte o seu agente para hanzo.ai/agents.
Leia mais

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.

O roteador é o registro
A Hanzo Cloud não tem arquivo OpenAPI versionado. A tabela de rotas viva é a fonte, projetada de três jeitos, e um teste de bijeção faz o build falhar se alguma projeção desviar. 1,362 operações em 963 paths, geradas por processo na hora da requisição.
