Únete a la beta de Hanzo y obtén 50 % de descuento en cualquier plan de pago

Comercio headless listo para compradores que son agentes

Los compradores se están volviendo agentes. Qué hace falta para que un LLM pueda leer, firmar y pagar sin una persona de por medio, y qué entrega Hanzo hoy para eso.

En esta página

El comercio headless separó la tienda del motor, para que cualquier interfaz pudiera vender por una sola API. Esa interfaz siempre fue algo que una persona veía: un sitio, una app, un kiosco.

La siguiente interfaz no ve. Lee. El comprador se está volviendo un agente: un programa con una meta, un presupuesto y un ciclo de herramientas, mandado a encontrar un plan, una GPU o una API de pagos. Le compra a quien puede entender y pagarle sin pedirle ayuda a una persona. Si su negocio necesita que alguien lea una página, cierre un aviso de cookies, abra un correo de confirmación o escriba un número de tarjeta, el agente se queda con el siguiente resultado.

Esa es la tesis. Una empresa tiene que ser legible y cobrable por un LLM sin un humano en el circuito. Legible significa que el agente puede aprender qué venden ustedes, cuánto cuesta y cómo arrancar. Cobrable significa que puede arrancar, pagar y recibir lo comprado por una API, solo.

Esto es lo que se necesita, punto por punto, con lo que Hanzo entrega hoy. Cada dirección de abajo responde sin una cuenta, así que ustedes pueden revisar cada afirmación por su cuenta.

Entregar cada página como Markdown

Un agente paga por cada token que lee. En hanzo.ai, agreguen .md a la dirección de una página y reciben la página como Markdown: hanzo.ai/pricing.md, hanzo.ai/about.md. Cada página nombra a su par en el encabezado con <link rel="alternate" type="text/markdown">, para que el agente no tenga que adivinar.

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

Publicar un índice desde el que un agente pueda empezar

Un agente que nunca oyó de ustedes necesita una dirección que lleve a todo. Publicamos tres.

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

Recibir a los rastreadores a propósito

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

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

Volver el precio un dato

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

Dejar que una billetera se registre

Un agente no tiene bandeja de entrada, ni celular, ni tarjeta. Tiene un par de claves. Por eso hanzo.id registra una billetera: se pide un nonce de un solo uso para la dirección de la billetera, se firma el mensaje que describe, se publica la firma y se cambia el código que regresa por un token.

Una billetera que Hanzo no ha visto antes recibe su propia cuenta y su propia organización, con la billetera como admin, 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 cupo chico para probar

Con el token, POST /v1/account/keys devuelve una clave sk-live- que le sobrevive. Guárdenla; los docs dicen que se muestra una sola vez.

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

Cobrar sin una persona: stablecoins por la API

POST /v1/billing/crypto/deposit toma una cadena, un token y un monto, y devuelve una dirección de depósito que sirve por 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 mandé dinero. Que el saldo se acredita cuando la cadena confirma es lo que dicen nuestros docs, no algo que yo haya corrido. Los planes y las suscripciones siguen siendo un checkout con tarjeta en el navegador. Lo que un agente necesita de nosotros es saldo, y el saldo tiene una API.

El flujo completo, en un solo archivo

Esto es hanzo.ai/agents#headless, abreviado. Cada línea está como la imprime esa página; dejé afuera el segundo comentario del encabezado, la llamada de opciones y la línea que imprime un prefijo de clave. Ustedes necesitan Node 22.18 o más nuevo y npm i viem, y después node headless.mts. Corrí este bloque contra producción con una billetera nueva: un token, una clave, una llamada gratis 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 viene, y todavía no está

Pasada una recarga, la forma correcta para un agente es pagar por solicitud. 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 de esa forma todavía, así que no lo voy a afirmar. Hasta que lo haga, un saldo recargado por la API es como un agente nos paga.

Por qué importa llegar a cero errores de rastreador

Nada de esto falla con ruido. Un agente que sigue un enlace de su sitemap y cae en un 404 no abre un ticket. Se queda con el siguiente resultado. Traten un error de rastreador como una tienda trata un checkout roto.

Bajé 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 mostrarles lo que ven los rastreadores de verdad. Hagan lo mismo en su sitio: pidan su sitemap con los agentes que ustedes están invitando y cuenten las respuestas que no son 200.

Esto ya lo vivimos

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

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

Eso eran máquinas del lado de la venta, encontrando a las audiencias que compraban y escribiendo los anuncios. El lado de la compra sigue. El mismo ciclo está por correr desde el otro extremo, y su negocio va a ser leído y pagado por una máquina, o una máquina lo va a pasar de largo.

Apunten su agente a hanzo.ai/agents.

Comentarios

Ingresa con tu Hanzo ID para comentar.

Cargando comentarios…

Leer más