Rendere il commercio headless leggibile agli LLM agentici
Chi compra sta diventando un agente. Che cosa serve perché un LLM possa leggere, firmare e pagare senza un essere umano nel mezzo, e che cosa Hanzo mette in campo oggi.

In questa pagina
Il commercio headless ha separato la vetrina dal motore, così che qualsiasi front end potesse vendere attraverso una sola API. Quel front end è sempre stato qualcosa che una persona guardava: un sito, un'app, un chiosco.
Il prossimo front end non guarda. Legge. Chi compra sta diventando un agente: un programma con un obiettivo, un budget e un ciclo di strumenti, mandato a trovare un piano, una GPU o un'API di pagamenti. Compra da chi riesce a capire e a pagare senza chiedere aiuto a un essere umano. Se la tua attività ha bisogno che una persona legga una pagina, chiuda un banner dei cookie, apra un'email di conferma o digiti un numero di carta, l'agente prende il risultato successivo.
Questa è la tesi. Un'attività deve essere leggibile e transazionabile da un LLM, senza alcun essere umano nel mezzo. Leggibile vuol dire che l'agente può imparare che cosa vendi, quanto costa e come si comincia. Transazionabile vuol dire che può cominciare, pagare e ricevere la cosa attraverso un'API, da solo.
Ecco che cosa serve, punto per punto, con quello che Hanzo rilascia oggi. Ogni indirizzo qui sotto risponde senza un account, così puoi verificare tu stesso ogni affermazione.
Servire ogni pagina come Markdown
Un agente paga ogni token che legge. Su hanzo.ai, aggiungi .md all'indirizzo di una pagina e ottieni la pagina come Markdown: hanzo.ai/pricing.md, hanzo.ai/about.md. Ogni pagina nomina la sua gemella nell'intestazione con <link rel="alternate" type="text/markdown">, così un agente non deve indovinare.
Il 26 ottobre ho scaricato la nostra pagina degli agenti in entrambi i modi: 528,810 byte come HTML, 17,465 come Markdown. Poi ho preso un URL ogni dodici nella nostra sitemap, 70 di 829, e ho chiesto la sua gemella. Tutti e 70 sono tornati 200 come text/markdown.
Pubblicare un indice da cui un agente possa partire
Un agente che non ha mai sentito parlare di te ha bisogno di un indirizzo che porti ovunque. Ne pubblichiamo tre.
- hanzo.ai/llms.txt è una mappa del sito in testo semplice. Dice che cos'è Hanzo, poi offre una sezione "For agents" con i passi esatti per ottenere una chiave, poi elenca le pagine, una riga ciascuna. docs.hanzo.ai/llms.txt fa lo stesso per la documentazione.
- api.hanzo.ai/v1 elenca ogni capacità che l'API serve, senza chiave, e api.hanzo.ai/v1/openapi.json ha ogni operazione con il suo schema. Per vendere, l'indice include una capacità
commerce: checkout, abbonamenti, fatture, webhook di pagamento e il catalogo della vetrina. - hanzo.ai/.well-known/api-catalog è il posto standard (RFC 9727) in cui un client guarda per primo. Punta al documento OpenAPI, alla documentazione e alla pagina di stato.
Accogliere i crawler di proposito
robots.txt è il primo file che qualsiasi crawler chiede. Il nostro nomina dodici crawler di IA uno per uno, tra cui GPTBot, ClaudeBot, Claude-User, PerplexityBot e Google-Extended, e li consente. Ricevono le stesse eccezioni di tutti gli altri: accesso, account, pagamento e i percorsi dell'API.
Bloccare i crawler di IA è una scelta legittima. Bloccarli perché un valore predefinito lo diceva non è affatto una scelta.
Rendere il prezzo un dato
Gli agenti confrontano. Un prezzo dentro un'immagine, o dietro un pulsante "contact us", non si può confrontare. api.hanzo.ai/v1/billing/plans non ha bisogno di una chiave e restituisce JSON: Free a 0, Pro a 2000 al mese, Team a 2500 per posto, con i limiti di ogni piano accanto. Gli importi sono in centesimi.
Lasciare che un portafoglio si registri
Un agente non ha una casella di posta, né un telefono, né una carta. Ha una coppia di chiavi. Perciò hanzo.id fa entrare un portafoglio: chiedi un nonce monouso per l'indirizzo del portafoglio, firma il messaggio che descrive, invia la firma e scambia il codice che torna con un token.
Un portafoglio che Hanzo non ha mai visto prima riceve un proprio account e una propria organizzazione, con il portafoglio come amministratore, sul piano Free. Il token dura sette giorni. Nessuno apre un'email né approva un dispositivo.
Una chiamata per una chiave, e un piccolo credito per provare
Con il token, POST /v1/account/keys restituisce una chiave sk-live- che gli sopravvive. Conservala; la documentazione dice che viene mostrata una sola volta.
Il piano Free è di 50 richieste al giorno su zen-free, senza carta, e ogni risposta dice che cosa resta in x-ratelimit-remaining. La mia prima chiamata ha risposto 200 con 49 rimasti.
Incassare senza un essere umano: stablecoin via API
POST /v1/billing/crypto/deposit prende una chain, un token e un importo, e restituisce un indirizzo di deposito valido per 24 ore. GET /v1/billing/crypto/options elenca ciò che accetta: le chain Base, Ethereum e Solana, e i token USDC e USDT.
Mi sono fermato all'indirizzo e non ho inviato denaro. Che il saldo venga accreditato quando la chain conferma è ciò che dice la nostra documentazione, non qualcosa che ho eseguito io. I piani e gli abbonamenti restano un checkout con carta nel browser. Ciò di cui un agente ha bisogno da noi è il saldo, e il saldo ha un'API.
L'intero flusso, in un solo file
Questo è hanzo.ai/agents#headless, abbreviato. Ogni riga è come la stampa quella pagina; ho lasciato fuori il secondo commento di intestazione, la chiamata delle opzioni e la riga che stampa un prefisso di chiave. Ti serve Node 22.18 o più recente e npm i viem, poi node headless.mts. Ho eseguito questo blocco contro la produzione con un portafoglio nuovo: un token, una chiave, una chiamata gratuita e un indirizzo di deposito 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 sta arrivando, e non è ancora qui
Oltre a una ricarica, la forma giusta per un agente è il pagamento a richiesta. Il server risponde 402 Payment Required con un prezzo, il client paga e riprova. Quello è x402. Non è attivo sulla nostra API: nessun endpoint di Hanzo quota un prezzo in quel modo, per ora, quindi non lo affermo. Finché non lo farà, un saldo ricaricato via API è il modo in cui un agente ci paga.
Perché zero errori dei crawler contano
Niente di tutto questo fallisce rumorosamente. Un agente che segue un link dalla tua sitemap verso un 404 non apre un ticket. Prende il risultato successivo. Tratta un errore del crawler come un negozio tratta una cassa rotta.
Ho scaricato quelle stesse 70 pagine del campione con la stringa user-agent di ClaudeBot e di nuovo con quella di Googlebot. Ognuna ha risposto 200 entrambe le volte. È un campione, non una garanzia, e una stringa user-agent non è l'indirizzo proprio del crawler, quindi non posso mostrarti che cosa vedono i crawler veri. Fai lo stesso sul tuo sito: chiedi la tua sitemap con gli agenti che stai invitando e conta le risposte che non sono 200.
Ci siamo già passati
Ho fondato questa azienda nel 2014, come Crowdstart; l'abbiamo rinominata Hanzo nel 2016. Il nostro primo cliente, SKULLY, ha raccolto $1 million su Indiegogo in circa 45 ore, ciò che TechCrunch ha chiamato la campagna Indiegogo interamente finanziata più rapida. Per come la vedo io, Hanzo è stato commercio nativo dell'IA fin dall'inizio: prima la nostra analitica, poi modelli che imparavano dai dati delle aziende sulla piattaforma. Nel 2017 siamo entrati in Techstars (il post di quel giugno), e ciò che dicevamo di stare costruendo era il commercio intelligente.
Nel 2020 Damon Motorcycles ha debuttato al CES con Hanzo a gestire la campagna. In 30 giorni la piattaforma ha venduto $8.4M di moto con $15,532 di spesa pubblicitaria, un ritorno di 541:1. I pubblici venivano ricostruiti dai nostri dati cliente ogni 12 ore, e set di annunci scritti dall'IA sono stati valutati Above Average su Facebook entro 36 ore. Il caso di studio ha il resto.
Quelle erano macchine sul lato della vendita, a trovare i pubblici che compravano e a scrivere gli annunci. Il lato dell'acquisto è il prossimo. Lo stesso ciclo sta per girare dall'altra estremità, e la tua attività sarà letta e pagata da una macchina, oppure scartata da una.
Punta il tuo agente su hanzo.ai/agents.
Leggi di più

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.

Il router è il registro
Hanzo Cloud non ha alcun file OpenAPI versionato. La tabella di route viva è la fonte, proiettata in tre modi, e un test di biiezione fa fallire la build se una proiezione deriva. 1,362 operazioni su 963 path, generate per processo al momento della richiesta.
