← Blog
29 de octubre, 2026 · 12 min

acceptance-record de x402: el veredicto firmado del comprador

Todos los receipts de x402 hasta hoy son una declaración del vendedor. El PR #4006 propone la mitad que falta: un registro en el que el comprador firma "accepted", "rejected" o "disputed" sobre una entrega concreta. Leímos la spec, verificamos sus vectores y corrimos una verificación de firma con librerías estándar sobre los 127.

El PR es una especificación y un archivo de vectores. No cambia ningún SDK ni mueve fondos. Lo abrió el 2026-10-27 el equipo de Shodai en CNS Labs, como x402-foundation/x402 #4006. Cuando lo leímos el 2026-10-29 seguía abierto, sin reviews, y su único comentario era un bot de Vercel pidiendo autorización de deploy.

Importa porque la siguiente discusión en pagos de agentes no es sobre el settlement. Es sobre qué pasa después de que el dinero se mueve y el resultado resulta estar mal.

Qué propone el PR

Un acceptance record es la decisión firmada de la contraparte sobre una entrega. La spec llama delivery issuer a quien firmó el registro de la entrega (en x402, el servidor del vendedor) y confirming party a quien decide (normalmente el comprador). La decisión es una de tres cadenas: accepted, rejected o disputed.

El body es un objeto JSON cerrado con exactamente once miembros: version, contextId, deliveryIssuer, confirmingParty, signer, authority, termsDigest, outcomeDigest, subject, disposition e issuedAt. Un miembro faltante o extra es un error. No hay contenedor de extensiones; un significado nuevo exige una nueva versión del body.

Dos formas de firma llevan el mismo body:

Un registro no es una afirmación aislada. Nombra su sujeto mediante un perfil, y el único perfil definido es x402ev/1, el formato de execution-evidence del PR #3682. El contextId del registro es la identidad de 32 bytes de ese registro de evidencia (evidenceRef), y su outcomeDigest debe igualar el digest que el vendedor ligó al contenido entregado.

Los términos se ligan aparte. termsDigest debe igualar el SHA-256 de la forma JCS de una offer firmada del mismo vendedor, de la extensión offer-and-receipt que cubrimos en la auditoría de receipts. La offer califica solo si su network, asset, payee y amount coinciden con el registro de evidencia.

Dónde encaja y de qué depende

Al 2026-10-29 todos los vecinos de este PR también están abiertos. Revisamos cada uno.

Así que el registro se verifica contra evidencia que todavía no forma parte de x402. La spec lo dice sin rodeos: su línea de estado es "Draft, version 0. Not ratified; no x402 body has accepted it." Sus identificadores, incluidos el nombre del dominio EIP-712 y el typ del JWS, están marcados como draft y pueden cambiar. Los autores también dicen que implementarán soporte en SDK solo cuando la especificación sea aceptada.

El resumen honesto: es un bloque bien especificado para un stack que aún no existe. Vale la pena leerlo igual, porque las decisiones de diseño son lo que va a sobrevivir.

Cinco decisiones que vale la pena leer con cuidado

1. El dominio EIP-712 no tiene cadena

La extensión offer-and-receipt fija el chainId del dominio en 1 para que la clave del vendedor sirva en cualquier red. Un acceptance lo firma en cambio la wallet del comprador. La spec afirma que las wallets suelen negarse a firmar cuando la cadena del dominio difiere de la red activa, y que la wallet que probaron sus autores lo hace.

Por eso el dominio lleva solo name y version. La cadena de la cuenta va dentro del mensaje firmado como signerChainId, y el verificador debe comprobar la firma en esa cadena y en ninguna otra, "no la red de pago ni la red en la que estaba la wallet". La spec también pide que el software de firma muestre signerChainId, porque el prompt de una wallet puede mostrar una red activa distinta a su lado.

Confirmamos la consecuencia directamente. El vector baseline verifica con el dominio de dos campos. Al re-hashear el mismo mensaje con chainId: 84532 agregado al dominio, el digest cambia y el firmante recuperado ya no coincide. Un firmante que agrega una cadena por costumbre produce un registro que ningún verificador conforme aceptará.

2. Una firma válida nunca basta

La autoridad es un paso aparte. Un verificador no debe reportar verified a menos que se resuelva el derecho del firmante a decidir por la confirming party sobre esta entrega. Existen dos perfiles.

payer/0 se resuelve cuando signer, confirmingParty y el payer del registro de evidencia son la misma cadena. declared-evidence/0 se resuelve solo cuando los trust inputs propios de la parte que confía contienen una entrada que coincide con la confirming party, el firmante, el perfil del sujeto y la referencia del sujeto. El registro no puede llevar esa entrada, y la spec dice que un vendedor que nombra a un delegado no la crea.

Esa segunda regla evita que la session key de un agente hable en silencio por una empresa. También significa que cualquier cosa más allá de "el payer lo firmó" exige que la parte que confía construya primero datos de confianza.

3. El vendedor no puede aceptar su propia entrega

El verificador debe reportar acceptance_signer_is_issuer si la confirming party o el firmante igualan al delivery issuer, o si la clave que verificó el registro está listada para el issuer o firmó el registro de evidencia. La comprobación cubre claves además de nombres, así que un vendedor no puede lavar una aceptación a través de una segunda identidad que comparta clave.

4. El silencio no significa nada

La spec reserva not_responded para "una respuesta era debida según los términos y no llegó" y afirma que esta versión nunca lo produce. Un registro ausente da no_record, sin disposition ni condiciones. Los verificadores no deben inferir aceptación, rechazo ni tardanza a partir de eso.

Es un rechazo deliberado del patrón "el silencio es consentimiento" que suelen adoptar los sistemas de auto-settle. Las ventanas de respuesta quedan para la siguiente revisión.

5. Liga los términos, no el recurso

La offer se compara por network, asset, payee y amount. La spec admite el hueco: x402ev/1 no define cómo se deriva su hash de la URL de la petición, así que el resourceUrl de la offer no se compara. "Dos offers de un mismo vendedor para recursos distintos con el mismo amount, asset, payee y network califican ambas."

Para un gateway de LLMs esto importa. Un vendedor que cotiza muchos modelos desde un mismo payee y un mismo asset presentará a menudo varias offers que coinciden en esos cuatro campos. Un acceptance verificado diría que el comprador aceptó una entrega con ese precio, no qué modelo ni qué petición la produjo.

Qué corrimos

La spec incluye su archivo de vectores dentro del PR. Lo descargamos del commit head del PR, 6b3fc41, y corrimos tres comprobaciones, además de la prueba de dominio descrita arriba.

Hash. El SHA-256 del archivo es 8c20c905…16ec5, idéntico al valor del encabezado de la spec. Nada se desfasó entre texto y corpus.

Anatomía. Hay 127 vectores sobre 25 casos de requisitos: 70 EIP-712, 56 JWS y uno sin registro. Los resultados esperados:

malformed        74
unknown          30
verified         12
key_unresolved   10
no_record         1

Solo 12 de 127 terminan en verified, el 9.4% del corpus. El resto prueba rechazos. La condición más común es acceptance_member_invalid (38 vectores), seguida de signature_invalid (19). De los 30 resultados unknown, 20 son evidence_unavailable y 8 son acceptance_authority_unresolved. Se lee como una spec escrita por gente que espera que la mayoría de los registros en la red estén mal.

Verificación de firma independiente. Escribimos un verificador solo de firma con eth_account para EIP-712 y el paquete cryptography para Ed25519, y lo corrimos sobre cada registro que parsea y llega al paso de firma. Son 114 vectores; 10 se detienen antes, en el paso de clave, y 2 son JSON no parseable por diseño. Comparamos "firma válida" con la condición signature_invalid del corpus.

95 coinciden. Los 19 que difieren tienen explicación. Cuatro son casos de cuenta de contrato que requieren una llamada ERC-1271 que no modelamos. Once son fallas de forma (un registro no cerrado, un payload que no es JCS, números o texto en codificación incorrecta) donde la firma en sí puede estar bien pero el registro se rechaza por otra razón.

Los cuatro restantes son los interesantes. En cada uno, una librería estándar acepta una firma que la spec manda rechazar:

Nada de esto es un bug de las librerías. Implementan recover y verify, no este perfil. El punto es que un equipo que conecta recover() a un handler pasará el camino feliz, pasará la mayor parte del corpus y aun así aceptará cuatro clases de entrada maleada. Vimos la misma familia de reglas, rechazo de high-s y codificaciones estrictas, en la auditoría de reglas de verificación exact EVM. Un registro que se guarda, se hashea o se usa como clave de base de datos por sus bytes de firma no puede tener dos escrituras válidas.

Límites de lo que revisamos

No corrimos los tres checkers de los autores. No ejercitamos ERC-1271 contra una wallet de contrato desplegada; la spec tampoco, que prueba las cuentas de contrato "solo con estado de cadena declarado". No verificamos por nuestra cuenta las serializaciones JCS más allá de lo que traen los vectores.

Los autores también revelan que sus tres checkers pasan 127 de 127, y que quedan diferencias de interpretación documentadas, entre ellas un ejemplo de escritura de network donde el checker TypeScript todavía difiere. Pasar este corpus, en sus palabras, no resuelve entradas que no cubre.

Qué significa para LLM4Agents

Nuestro producto es un gateway medido. En el vocabulario de la spec somos el delivery issuer: devolvemos una respuesta y el agente comprador decide si era lo que pagó. Hoy esa decisión se queda en los logs del propio agente comprador. Este registro es la primera propuesta que permitiría que viva en un artefacto firmado que ambas partes pueden verificar.

De ahí salen tres consecuencias.

Primero, habilita manejar disputas sin un humano. Un agente que paga por llamada y recibe un rechazo, un stream truncado o el modelo equivocado podría emitir un registro firmado rejected o disputed que nombre nuestra propia evidencia de entrega firmada. Eso le da a un revisor, o a una política automática, un conjunto acotado de hechos para comprobar en lugar de una queja en texto libre.

Segundo, es una amenaza para las afirmaciones laxas. Si los acceptance records se vuelven comunes, "el comprador lo recibió" pasa a ser comprobable en ambos sentidos. Todo lo que digamos en nuestros reportes sobre entregas debe ser consistente con evidencia que podrían pedirnos firmar.

Tercero, el hueco del recurso es nuestro problema. Como el binding de la offer ignora el recurso, una disputa sobre la respuesta de un modelo podría respaldarse con una offer de otro modelo al mismo precio. Mientras el formato de evidencia no defina cómo nombra la petición, cualquier registro que toque nuestros endpoints prueba menos de lo que parece.

Algo que no hace es cambiar el settlement. Un acceptance verificado no deshace un pago. La spec lista "release, refund or reputation" como consecuencias fuera de esta versión. De nuestro lado, nada cambia en el flujo 402, la ruta del facilitator ni la facturación.

Cómo mantenerse en la frontera

Pasos concretos, en orden. Ninguno requiere una decisión nuestra hoy.

Uno. Seguir la cadena de dependencias, no solo este PR. #4006 solo puede verificar contra evidencia de #3682 y una offer de offer-and-receipt. Si #3682 cambia su referencia de evidencia o sus campos, los vectores cambian. Volveremos a revisar el estado de los cuatro PRs y el hash de los vectores cuando alguno se mueva.

Dos. Construir ya el hábito de codificación estricta. Cuando escribamos cualquier verificador de artefactos firmados de agentes, copiar las reglas de perfil que las librerías estándar omiten: rechazar high-s, exigir 27 o 28, exigir hex en minúsculas y rechazar puntos Ed25519 de orden pequeño. El corpus de este PR es un set de pruebas gratuito para esas reglas.

Tres. Decidir qué diría nuestra evidencia de entrega antes de que alguien lo pida. Si más adelante emitimos registros x402ev/1, los campos que importan a un comprador son el digest del contenido y el binding de la petición. Hay que resolver primero cómo derivamos un hash de la petición, porque es justo el hueco que señala la spec.

Cuatro. Mantener compatible nuestro propio proceso de disputas. Sea lo que sea lo que ofrezcamos hoy a los compradores, un mensaje firmado con disposition, referencia al sujeto y outcome digest es la forma a la que apuntar, para poder adjuntar un registro futuro sin rediseñar.

Cinco. Mantenerse fuera del lado de la firma hasta que se ratifique la spec. Identificadores como el nombre del dominio y el typ del JWS están marcados como draft. Lanzar un firmante ahora significaría lanzar algo que podría no verificar después. Leer y probar el corpus no cuesta nada; desplegar contra él es prematuro.

Para el panorama de cómo encajan receipts, escrow y discovery, ver la auditoría de la capa de extensiones y la auditoría de auth-capture escrow. Los acceptance records son la contraparte del lado comprador de la primera y el disparador que le falta a la segunda.

Paga por llamada, en stablecoins, sobre una API compatible con OpenAI

Un 402, una firma, 345+ modelos. Sin suscripción, sin mínimo.

Registra tu agente