Únete a la beta de Hanzo y consigue un 50 % de descuento en cualquier plan de pago

El comercio headless cuando quien compra es un LLM

Quien compra empieza a ser un agente. Qué exige que un LLM pueda leer, firmar y pagar sin nadie en medio, y qué ofrece Hanzo hoy para conseguirlo.

En esta página

El comercio headless separó el escaparate del motor, de forma que cualquier frontal pudiera vender a través de una sola API. Ese frontal siempre fue algo que una persona miraba: un sitio, una aplicación, un quiosco.

El siguiente frontal no mira. Lee. Quien compra se está convirtiendo en un agente: un programa con un objetivo, un presupuesto y un bucle de herramientas, enviado a buscar un plan, una GPU o una API de pagos. Compra al vendedor al que puede entender y pagar sin pedir ayuda a una persona. Si vuestro negocio necesita que alguien lea una página, cierre un aviso de cookies, abra un correo de confirmación o teclee un número de tarjeta, el agente se queda con el siguiente resultado.

Esa es la tesis. Un negocio tiene que ser legible y transaccionable por un LLM sin ninguna persona en el proceso. Legible quiere decir que el agente puede saber qué vendéis, cuánto cuesta y cómo se empieza. Transaccionable quiere decir que puede empezar, pagar y recibir lo comprado a través de una API, solo.

Esto es lo que hace falta, punto por punto, junto con lo que Hanzo publica hoy. Todas las direcciones de abajo responden sin cuenta, así que podéis comprobar cada afirmación por vuestra cuenta.

Servir cada página en Markdown

Un agente paga cada token que lee. En hanzo.ai, añadid .md a la dirección de una página y obtenéis la página en Markdown: hanzo.ai/pricing.md, hanzo.ai/about.md. Cada página nombra a su gemela en la cabecera con <link rel="alternate" type="text/markdown">, así el agente no tiene que adivinar.

El 26 de octubre descargué nuestra página de agentes de las dos maneras: 528,810 bytes en HTML, 17,465 en Markdown. Después tomé una de cada doce URL del sitemap, 70 de 829, y pedí su gemela. Las 70 volvieron 200 como text/markdown.

Publicar un índice del que pueda partir un agente

Un agente que nunca ha oído hablar de vosotros necesita una dirección que lleve a todas partes. Publicamos tres.

  • hanzo.ai/llms.txt es un mapa del sitio en texto plano. Dice qué es Hanzo, luego ofrece una sección "For agents" con los pasos exactos para conseguir una clave, y después lista las páginas, una línea cada una. docs.hanzo.ai/llms.txt hace lo mismo con la documentación.
  • api.hanzo.ai/v1 enumera cada capacidad que sirve la API, sin clave, y api.hanzo.ai/v1/openapi.json tiene cada operación con su esquema. Para vender, el índice incluye una capacidad commerce: pago, suscripciones, facturas, webhooks de pago y el catálogo del escaparate.
  • hanzo.ai/.well-known/api-catalog es el lugar estándar (RFC 9727) donde un cliente mira primero. Apunta al documento OpenAPI, a la documentación y a la página de estado.

Dar la bienvenida a los rastreadores a propósito

robots.txt es el primer fichero que pide cualquier rastreador. El nuestro nombra doce rastreadores de IA uno por uno, entre ellos GPTBot, ClaudeBot, Claude-User, PerplexityBot y Google-Extended, y los permite. Reciben las mismas excepciones que los demás: inicio de sesión, cuenta, pago y las rutas de la API.

Bloquear a los rastreadores de IA es una decisión legítima. Bloquearlos porque un valor por defecto lo decía no es decisión ninguna.

Hacer del precio un dato

Los agentes comparan. Un precio dentro de una imagen, o detrás de un botón de "contact us", no se puede comparar. api.hanzo.ai/v1/billing/plans no necesita clave y devuelve JSON: Free a 0, Pro a 2000 al mes, Team a 2500 por asiento, con los límites de cada plan al lado. Los importes están en céntimos.

Dejar que un monedero se registre

Un agente no tiene bandeja de entrada, ni móvil, ni tarjeta. Tiene un par de claves. Por eso hanzo.id da de alta un monedero: se pide un nonce de un solo uso para la dirección del monedero, se firma el mensaje que describe, se envía la firma y se cambia el código que vuelve por un token.

Un monedero que Hanzo no ha visto antes obtiene su propia cuenta y su propia organización, con el monedero como administrador, en el plan Free. El token dura siete días. Nadie abre un correo ni aprueba un dispositivo.

Una llamada para una clave, y un pequeño margen para probar

Con el token, POST /v1/account/keys devuelve una clave sk-live- que le sobrevive. Guardadla; la documentación dice que se muestra una sola vez.

El plan Free son 50 peticiones al día en zen-free, sin tarjeta, y cada respuesta dice lo que queda en x-ratelimit-remaining. Mi primera llamada respondió 200 con 49 restantes.

Cobrar sin ninguna persona: monedas estables por la API

POST /v1/billing/crypto/deposit recibe una cadena, un token y un importe, y devuelve una dirección de depósito válida durante 24 horas. GET /v1/billing/crypto/options lista lo que acepta: las cadenas Base, Ethereum y Solana, y los tokens USDC y USDT.

Me detuve en la dirección y no envié dinero. Que el saldo se abona cuando la cadena confirma es lo que dice nuestra documentación, no algo que yo haya ejecutado. Los planes y las suscripciones siguen siendo un pago con tarjeta en el navegador. Lo que un agente necesita de nosotros es saldo, y el saldo tiene una API.

Todo el flujo, en un solo fichero

Esto es hanzo.ai/agents#headless, abreviado. Cada línea está como la imprime esa página; dejé fuera el segundo comentario de cabecera, la llamada a las opciones y la línea que imprime un prefijo de clave. Necesitáis Node 22.18 o posterior y npm i viem, y después node headless.mts. Ejecuté este bloque contra producción con un monedero nuevo: un token, una clave, una llamada gratuita y una dirección de depósito de 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á en camino, y todavía no está aquí

Más allá de una recarga, la forma adecuada para un agente es pagar por petición. El servidor responde 402 Payment Required con un precio, el cliente paga y reintenta. Eso es x402. No está activo en nuestra API: ningún endpoint de Hanzo cotiza un precio así todavía, así que no voy a afirmarlo. Hasta que lo haga, un saldo recargado por la API es la forma en que un agente nos paga.

Por qué importan cero errores de rastreador

Nada de esto falla con ruido. Un agente que sigue un enlace de vuestro sitemap y cae en un 404 no abre un ticket. Se queda con el siguiente resultado. Tratad un error de rastreador como una tienda trata una caja de cobro rota.

Descargué esas mismas 70 páginas de la muestra con la cadena user-agent de ClaudeBot y otra vez con la de Googlebot. Todas respondieron 200 las dos veces. Es una muestra, no una garantía, y una cadena user-agent no es la dirección propia del rastreador, así que no puedo mostraros lo que ven los rastreadores reales. Haced lo mismo en vuestro sitio: pedid el sitemap con los agentes que estáis invitando y contad las respuestas que no son 200.

Ya estuvimos aquí

Fundé esta empresa en 2014, como Crowdstart; la renombramos Hanzo en 2016. Nuestro primer cliente, SKULLY, recaudó $1 million en Indiegogo en unas 45 horas, lo que TechCrunch llamó la campaña de Indiegogo financiada por completo más rápida. A mi juicio Hanzo fue comercio nativo de IA desde el principio: primero nuestra propia analítica, luego modelos que aprendían de los datos de las empresas de la plataforma. En 2017 entramos en Techstars (la entrada de aquel junio), y lo que decíamos que estábamos construyendo era comercio inteligente.

En 2020 Damon Motorcycles se presentó en el CES con Hanzo a cargo de la campaña. En 30 días la plataforma vendió $8.4M en motos con $15,532 de gasto publicitario, un retorno de 541:1. Las audiencias se reconstruían con nuestros datos de clientes cada 12 horas, y los conjuntos de anuncios escritos por IA quedaron en Above Average en Facebook en menos de 36 horas. El caso práctico tiene el resto.

Eso eran máquinas en el lado de la venta, encontrando a las audiencias que compraban y redactando los anuncios. El lado de la compra es el siguiente. El mismo bucle está a punto de correr desde el otro extremo, y vuestro negocio será leído y pagado por una máquina, o una máquina lo dejará pasar.

Apuntad vuestro agente a hanzo.ai/agents.

Comentarios

Entra con tu Hanzo ID para comentar.

Cargando comentarios…

Leer más