Agent Output Verifier by SarnAI
Verifique a saída de um agente antes de pagar: JSON Schema + regras, recibos assinados. Grátis durante o lançamento.
Servidor MCP hospedado
npx add-mcp 'https://verify.sarnai.dev/mcp'Instala no Claude Code, Codex, Cursor e outros
Documentação
Agent Output Verifier by SarnAI
Verifique independentemente a saída de um agente antes de pagar.
Verificações independentes e determinísticas para faturas, entregas e qualquer saída estruturada: estrutura,
formatos, intervalos e regras entre campos, como "os totais das linhas devem ser iguais ao total", de acordo com seus
próprios requisitos, escritos em JSON Schema padrão mais regras opcionais. Retorna aprovado/reprovado, a porcentagem
de verificações aprovadas, dicas de correção e uma atestação assinada com Ed25519 e um recibo que qualquer pessoa pode verificar
com nossa chave pública. Vendedores: verifiquem sua própria saída antes de enviá-la e entreguem-na com uma
atestação assinada e recibo. Sem cadastro: pague por chamada com x402 v2, em USDC na Base ou Solana.
Grátis durante o lançamento: 1.000 verificações por dia por chamador via MCP e REST, compartilhadas; o valor real está em
/llms.txt. Agent Scores são pagos apenas: $0,01 por consulta.
Agent Output Verifier faz parte da SarnAI, um ecossistema de produtos para agentes que trabalham entre si, com o Agent Discovery Board (https://board.sarnai.dev/guide) e Agent Scores.
Este repositório é documentação e metadados públicos para o serviço hospedado — não há código-fonte
para instalar ou executar aqui. O serviço em si é uma API ao vivo em
https://verify.sarnai.dev. O endereço original, https://fastapi-service-5ag4.onrender.com, continua funcionando com as mesmas respostas e chaves; recibos emitidos antes da mudança apontam para ele.
Endpoints
| Finalidade | Endpoint |
|---|---|
| MCP (HTTP Streamable) | https://verify.sarnai.dev/mcp |
| Verificar uma saída (REST) | POST https://verify.sarnai.dev/verify/schema |
| Verificar uma entrega (REST; a mesma verificação sob um segundo nome) | POST https://verify.sarnai.dev/verify/deliverable |
| Consultar a pontuação de confiança de um agente (REST) | GET https://score.sarnai.dev/score/{agent_id} |
| Histórico gratuito de aprovado/reprovado de um agente (REST) | GET https://verify.sarnai.dev/reputation/{agent_id} |
| Verificação de atividade | GET https://verify.sarnai.dev/health |
| Cartão do agente (descoberta) | GET https://verify.sarnai.dev/.well-known/agent-card.json |
| Manifesto de descoberta x402 | GET https://verify.sarnai.dev/.well-known/x402 |
| llms.txt | GET https://verify.sarnai.dev/llms.txt |
| Documentação interativa da API | GET https://verify.sarnai.dev/docs |
Duas ferramentas MCP são expostas no mesmo endpoint: verify_schema (POST /verify/schema) e
get_verification_record (GET /score/{agent_id}).
Preços e redes
Após a franquia gratuita, cada chamada é paga com x402 v2 em USDC. Agent Scores são sempre pagos: $0,01. Nenhuma conta ou cadastro é necessário.
| Rota | Preço | Redes |
|---|---|---|
POST /verify/schema | $0,02 USDC | Base (eip155:8453), Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp) |
POST /verify/deliverable | $0,02 USDC | Base (eip155:8453), Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp) |
GET /score/{agent_id} | $0,01 USDC | Base (eip155:8453), Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp) |
/.well-known/x402 e o cabeçalho Payment-Required de uma resposta 402 ao vivo sempre carregam os
preços exatos ao vivo, endereços de pagamento e endereços de ativos — trate-os como a fonte da verdade, e
esta tabela como uma referência rápida.
Três cabeçalhos carregam o pagamento em si, por nome: uma solicitação não paga recebe HTTP 402 com o preço e
os requisitos de pagamento no cabeçalho de resposta Payment-Required; uma solicitação paga carrega seu pagamento
assinado no cabeçalho de solicitação Payment-Signature; uma resposta liquidada carrega o recibo de liquidação
no cabeçalho de resposta Payment-Response. Um resultado fail é uma resposta completa e válida para a
pergunta feita e é cobrado o mesmo que um pass — o trabalho (executar cada verificação) é idêntico
de qualquer forma.
Franquia gratuita
A verificação é grátis durante o lançamento. Cada cliente, identificado pelo endereço IP da solicitação (não há cadastro
ou conta), recebe uma franquia diária de verificações, compartilhada pela ferramenta MCP verify_schema e pelas rotas REST
POST /verify/schema e POST /verify/deliverable. A franquia é uma configuração, não uma constante: os números
ao vivo são declarados em um só lugar, na seção "Franquia gratuita" de /llms.txt.
Uma solicitação REST válida e não paga é atendida gratuitamente enquanto a franquia durar, e a resposta informa o que resta nos
cabeçalhos Free-Allowance-Remaining, Free-Allowance-Limit e Free-Allowance-Resets. Uma solicitação
grande demais, uma que carrega um pagamento e qualquer solicitação após a franquia do dia ser usada, vai
para pagamento como de costume (HTTP 402). Uma solicitação inválida nunca é direcionada ao pagamento: ela recebe um erro de validação (HTTP 422,
veja "Erros" abaixo). Verificações gratuitas contam para a pontuação e reputação de um agente exatamente como as pagas.
Agent Scores são sempre pagos apenas: GET /score/{agent_id} e a ferramenta MCP get_verification_record
custam $0,01 por consulta, sem acesso gratuito.
O corpo 402 de uma chamada REST não paga relata o estado real deste chamador, ao vivo: available,
remaining_calls_today e resets_at (a próxima meia-noite UTC). Quando a franquia de um cliente para o dia é
usada, available vira false e o corpo carrega "code": "free_trial_exhausted":
{
"free_trial": {
"available": false,
"via": "mcp+rest",
"transport": "streamable-http",
"url": "https://verify.sarnai.dev/mcp",
"tool": "verify_schema",
"calls_per_client_per_day": "<N>",
"remaining_calls_today": 0,
"resets_at": "<next UTC midnight>",
"client": "identified by request IP address; the same address's REST and MCP verification calls share one allowance",
"rest_free": true,
"launch": true,
"message": "Free during launch. This client's <N> free checks for today are used up (REST and MCP share them); they reset at <next UTC midnight>. Payment applies until then.",
"code": "free_trial_exhausted"
}
}
O mesmo código free_trial_exhausted acompanha o próprio resultado de pagamento-necessário da ferramenta MCP (em _meta)
quando uma chamada de ferramenta é feita após a franquia daquele cliente para o dia acabar. Após a franquia (ou quando ela é
usada para o dia), uma chamada exige um pagamento x402 carregado no _meta da solicitação MCP sob
x402/payment, ou um cabeçalho de pagamento x402 padrão no REST.
Erros
Todo erro diferente de um erro de pagamento tem uma forma, no REST e nos resultados de ferramentas MCP:
{
"code": "invalid_request",
"message": "The request is invalid: rules[0].type is not one of the allowed values. Nothing was charged; fix the field(s) and send it again.",
"status": 422,
"retryable": false,
"charged": false,
"next_actions": [{"action": "fix_request_fields", "fields": ["rules[0].type"]}],
"fields": [{"field": "rules[0].type", "problem": "is not one of the allowed values", "fix": "Use one of: 'sum_equals', 'gte', 'lte', 'exists_in' or 'unique'."}]
}
code é estável, retryable indica se a mesma solicitação pode ter sucesso em uma nova tentativa (um erro repetível também tem
retry_after), e um erro nunca é cobrado. Sua entrada nunca é ecoada de volta. Uma solicitação inválida (JSON ruim, um
campo ausente, um tipo de regra desconhecido, um id com mais de 200 caracteres) é rejeitada com 422 antes do pagamento, então
nunca é informado "pagamento necessário"; um POST sem corpo algum é a sonda de descoberta x402 e ainda recebe o 402. Um corpo
acima do limite de tamanho recebe um 413 que informa o limite. A tabela completa de códigos e os códigos de erro de pagamento estão nas
seções "Erros" e "Erros de pagamento" de /llms.txt e na descrição do OpenAPI.
Verificando um recibo, passo a passo
Toda resposta de /verify/schema e /score/{agent_id} carrega uma seção verify assinada e
um bloco attestation, por exemplo:
"verify": {
"public_key_url": "https://verify.sarnai.dev/.well-known/agent-card.json",
"key_version": "v1-2026-09c",
"canonicalization": "RFC 8785 (JCS)",
"hash_algorithm": "SHA-256",
"service": "Agent Output Verifier",
"mcp": "https://verify.sarnai.dev/mcp"
},
"attestation": {
"algorithm": "Ed25519",
"key_version": "v1-2026-09c",
"timestamp": "2026-10-01T01:01:09.606993+00:00",
"signature": "8UexIObSAwQxjj8/rbEpLRgbYY6XO+wg5+b0D8I3BRfbOxl+ouwcKNPE/hp+9p37Tm2wwdmCqPjBgOMLGQtGAQ=="
}
Nada além da própria resposta e verify.public_key_url é necessário:
-
Busque a chave pública.
GETo cartão do agente emverify.public_key_url. Sua matrizcapabilities.extensionstem uma entrada cujouricomeça comurn:json-schema-verifier:extension:attestation:; seuparams.publicKeyslista cada chave que o serviço já usou para assinar, cada uma{keyVersion, algorithm, publicKey}(a chave pública Ed25519 bruta de 32 bytes, codificada em base64). Escolha a entrada cujokeyVersionseja igual aresponse.attestation.key_version. O cartão do agente ao vivo é sempre a fonte da verdade; esta é uma cópia de conveniência da chave atual, que vale a pena reconfirmar contra o cartão do agente ao vivo primeiro:{ "keyVersion": "v1-2026-09c", "algorithm": "Ed25519", "publicKey": "d2IHH0/Xduci/5J8IewYmauUvgqf+gFIeCEXDwjHUhw=", "publicKeyEncoding": "base64 of the raw 32-byte Ed25519 public key" } -
Reconstrua o documento assinado. É um objeto JSON com exatamente estas cinco chaves:
{ "context": "json-schema-verifier/verify-schema-attestation/v1", "algorithm": "Ed25519", "key_version": "<attestation.key_version>", "attested_at": "<attestation.timestamp>", "response": { "...": "the full response, with the attestation field removed" } }contextéjson-schema-verifier/verify-schema-attestation/v1para respostas/verify/schemae para respostas/verify/deliverable(a mesma verificação sob um segundo nome; não há contexto de entrega separado), oujson-schema-verifier/trust-score-attestation/v1para respostas/score/{agent_id}. -
Canonicalize com RFC 8785 (JCS): bytes UTF-8 do documento JSON acima, chaves de objeto classificadas recursivamente, sem espaços em branco insignificantes, números impressos da forma que ECMAScript os imprime (
1.0vira1). -
Verifique a assinatura Ed25519 (
response.attestation.signature, decodificada em base64) sobre esses bytes canônicos, usando a chave pública do passo 1. -
Verifique os quatro hashes do recibo, cada um SHA-256 sobre o JSON canônico RFC 8785 (JCS) do valor nomeado:
output_hash(desubmitted_output),schema_hash(deexpected_schema),rules_hash(de{"bounds": ..., "rules": ...}) erequest_hash(de toda a solicitação como analisada, apenas detalhe completo). Recalcule-os você mesmo e compare — é isso que vincula o recibo aos dados exatos que foram verificados.request_hashpreenche cada campo por regra e por limite com seu próprio padrão antes de aplicar hash — umtoleranceomitido tem hash como1e-9, umif_presentomitido comofalse, umequals_field/equals/other_field/value/in_field/valuesomitido comonull— então duas solicitações cujas regras diferem apenas em quais campos opcionais foram digitados ainda têm hash idêntico aqui.rules_hashé o oposto: ele aplica hash exatamente e apenas aos campos que você forneceu, nada preenchido por padrão.
Um verificador Python mínimo, usando apenas a biblioteca padrão mais cryptography:
import base64, json
from decimal import Decimal
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
def es_number(x: float) -> str:
"""A float exactly as ECMAScript / RFC 8785 (JCS) print it (1.0 -> "1", 1e-7 -> "1e-7")."""
if x == 0:
return "0"
sign = "-" if x < 0 else ""
digits_tuple = Decimal(repr(abs(x))).as_tuple()
digits = "".join(map(str, digits_tuple.digits)).rstrip("0") or "0"
n = len(digits_tuple.digits) + digits_tuple.exponent # position of the decimal point
k = len(digits)
if k <= n <= 21:
body = digits + "0" * (n - k)
elif 0 < n <= 21:
body = digits[:n] + "." + digits[n:]
elif -6 < n <= 0:
body = "0." + "0" * (-n) + digits
else:
e = n - 1
body = digits[0] + ("." + digits[1:] if k > 1 else "") + f"e{'+' if e >= 0 else '-'}{abs(e)}"
return sign + body
def canonical(v) -> str:
if isinstance(v, bool):
return "true" if v else "false"
if isinstance(v, float):
return es_number(v)
if isinstance(v, dict):
return "{" + ",".join(f"{canonical(k)}:{canonical(v[k])}" for k in sorted(v)) + "}"
if isinstance(v, list):
return "[" + ",".join(canonical(x) for x in v) + "]"
return json.dumps(v, ensure_ascii=False) # covers None, int, str
def verify(response: dict, public_key_b64: str, context: str) -> bool:
att = response["attestation"]
body = {k: v for k, v in response.items() if k != "attestation"}
message = canonical({
"context": context,
"algorithm": att["algorithm"],
"key_version": att["key_version"],
"attested_at": att["timestamp"],
"response": body,
}).encode("utf-8")
key = Ed25519PublicKey.from_public_bytes(base64.b64decode(public_key_b64))
try:
key.verify(base64.b64decode(att["signature"]), message)
return True
except Exception:
return False
Testado contra ambos os recibos no exemplo prático abaixo — ambos verificam True.
Vetores de teste para request_hash
request_hash é o SHA-256 (hex minúsculo, dos bytes UTF-8) do JSON canônico RFC 8785 da solicitação como analisada: os oito campos task_id, agent_id, expected_schema, submitted_output, strict_content_check, bounds, rules e enforce_rules, com cada padrão preenchido, incluindo cada campo por regra e por limite que você deixou de fora. Um campo de regra omitido tem hash como equals_field/equals/other_field/value/in_field/values: null, tolerance: 1e-9, if_present: false; um campo de limite omitido tem hash como if_present: false. detail não faz parte dele (ele apenas escolhe a forma da resposta, e respostas compactas deixam request_hash de fora, então chame com o "detail": "full" padrão para vê-lo). Isso difere de propósito de rules_hash, que aplica hash apenas aos campos que você forneceu.
Cada vetor abaixo fornece o corpo da solicitação que você faria POST, a forma canônica exata que recebe hash e o hash resultante. Eles são reais: faça POST da solicitação para /verify/schema (ou /verify/deliverable) e o request_hash da resposta é o valor mostrado. Os mesmos vetores estão no cartão do agente (/.well-known/agent-card.json, extensão de atestação, requestHashTestVectors).
Vetor 1: mínimo. Sem regras ou limites: cada campo opcional assume seu padrão.
Solicitação:
{
"task_id": "tv-1",
"expected_schema": {
"type": "object",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean"
}
}
},
"submitted_output": {
"ok": true
}
}
Forma canônica (uma linha, sem espaços em branco):
{"agent_id":null,"bounds":null,"enforce_rules":false,"expected_schema":{"properties":{"ok":{"type":"boolean"}},"required":["ok"],"type":"object"},"rules":null,"strict_content_check":false,"submitted_output":{"ok":true},"task_id":"tv-1"}
request_hash:
5366542000b4226c5efb411c15ec8f0aac3c63844343997aa1bcc3c585086f97
Vetor 2: regras-e-limites. Um limite e duas regras com enforce_rules ativado: cada campo por regra e por limite que você deixou de fora é preenchido com seu padrão (tolerância 1e-9, if_present falso, as referências não usadas nulas), e JCS imprime 10.0 como 10.
Solicitação:
{
"task_id": "tv-2",
"agent_id": "seller-7",
"expected_schema": {
"type": "object",
"required": [
"items",
"total"
],
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"price"
],
"properties": {
"id": {
"type": "string"
},
"price": {
"type": "number"
}
}
}
},
"total": {
"type": "number"
}
}
},
"submitted_output": {
"total": 30.5,
"items": [
{
"id": "a",
"price": 10.0
},
{
"id": "b",
"price": 20.5
}
]
},
"bounds": {
"items[].price": {
"min": 0,
"max": 100
}
},
"rules": [
{
"type": "sum_equals",
"field": "items[].price",
"equals_field": "total",
"tolerance": 0.01
},
{
"type": "unique",
"field": "items[].id"
}
],
"enforce_rules": true
}
Forma canônica (uma linha, sem espaços em branco):
{"agent_id":"seller-7","bounds":{"items[].price":{"if_present":false,"max":100,"min":0}},"enforce_rules":true,"expected_schema":{"properties":{"items":{"items":{"properties":{"id":{"type":"string"},"price":{"type":"number"}},"required":["id","price"],"type":"object"},"type":"array"},"total":{"type":"number"}},"required":["items","total"],"type":"object"},"rules":[{"equals":null,"equals_field":"total","field":"items[].price","if_present":false,"in_field":null,"other_field":null,"tolerance":0.01,"type":"sum_equals","value":null,"values":null},{"equals":null,"equals_field":null,"field":"items[].id","if_present":false,"in_field":null,"other_field":null,"tolerance":1e-9,"type":"unique","value":null,"values":null}],"strict_content_check":false,"submitted_output":{"items":[{"id":"a","price":10},{"id":"b","price":20.5}],"total":30.5},"task_id":"tv-2"}
request_hash:
1aa064d21a2c1487d5d319ded6efec48c18dfee45b93f741266b1f491c2e099f
Vetor 3: estrito-e-unicode. strict_content_check ativado, uma regra com if_present, texto não ASCII mantido como UTF-8 literal, e as formas numéricas JCS 1.0 -> 1, 1e21 -> 1e+21, 0.000001 -> 0.000001.
Solicitação:
{
"task_id": "tv-3",
"expected_schema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"n": {
"type": "number"
}
}
},
"submitted_output": {
"n": 1.0,
"name": "café ✓",
"z": [
1e+21,
1e-06
]
},
"strict_content_check": true,
"rules": [
{
"type": "gte",
"field": "n",
"value": 0,
"if_present": true
}
]
}
Forma canônica (uma linha, sem espaços em branco):
{"agent_id":null,"bounds":null,"enforce_rules":false,"expected_schema":{"properties":{"n":{"type":"number"},"name":{"type":"string"}},"type":"object"},"rules":[{"equals":null,"equals_field":null,"field":"n","if_present":true,"in_field":null,"other_field":null,"tolerance":1e-9,"type":"gte","value":0,"values":null}],"strict_content_check":true,"submitted_output":{"n":1,"name":"café ✓","z":[1e+21,0.000001]},"task_id":"tv-3"}
request_hash:
037abd3fe4643c14e68d9fd57ca93b460c177c5372f058aa2920a2ef120c0432
Para reproduzi-los sem nosso código, este trecho Python autossuficiente (apenas biblioteca padrão) recebe um corpo de solicitação e retorna a forma canônica e o hash. Ele implementa as regras RFC 8785 que estes vetores exercitam: chaves classificadas por unidade de código UTF-16, sem espaços em branco, strings como UTF-8 literal e números impressos como ECMAScript os imprime (10.0 é 10, 1e21 é 1e+21, 1e-9 permanece 1e-9). Qualquer biblioteca JCS (por exemplo, o pacote rfc8785) dá o mesmo resultado.
import hashlib, json
from decimal import Decimal
RULE = dict(equals_field=None, equals=None, other_field=None, value=None, in_field=None, values=None,
tolerance=1e-9, if_present=False)
BOUND = dict(min=None, max=None, if_present=False)
def number(x): # ECMAScript Number-to-string, as RFC 8785 requires
if isinstance(x, int):
return str(x)
sign, digits, exp = Decimal(repr(x)).as_tuple()
raw = "".join(map(str, digits))
n = len(raw) + exp # position of the decimal point
digits = raw.rstrip("0")
if not digits:
return "0"
k, s = len(digits), "-" if sign else ""
if k <= n <= 21: return s + digits + "0" * (n - k)
if 0 < n <= 21: return s + digits[:n] + "." + digits[n:]
if -6 < n <= 0: return s + "0." + "0" * -n + digits
e = n - 1
return s + digits[0] + ("." + digits[1:] if k > 1 else "") + "e" + ("+" if e > 0 else "-") + str(abs(e))
def jcs(v):
if v is None: return "null"
if v is True: return "true"
if v is False: return "false"
if isinstance(v, str): return json.dumps(v, ensure_ascii=False)
if isinstance(v, (int, float)): return number(v)
if isinstance(v, list): return "[" + ",".join(map(jcs, v)) + "]"
return "{" + ",".join(json.dumps(k, ensure_ascii=False) + ":" + jcs(v[k])
for k in sorted(v, key=lambda k: k.encode("utf-16-be"))) + "}"
def request_hash(req): # req: the request body you POST; returns (canonical form, hash)
full = {
"task_id": req["task_id"], "agent_id": req.get("agent_id"),
"expected_schema": req["expected_schema"], "submitted_output": req["submitted_output"],
"strict_content_check": req.get("strict_content_check", False),
"bounds": {p: {**BOUND, **b} for p, b in req["bounds"].items()} if req.get("bounds") else None,
"rules": [{**RULE, **r} for r in req["rules"]] if req.get("rules") else None,
"enforce_rules": req.get("enforce_rules", False),
}
return jcs(full), hashlib.sha256(jcs(full).encode("utf-8")).hexdigest()
Exemplo prático: verificação de fatura
Ambas as chamadas abaixo são genuínas, ao vivo, pagas contra o serviço de produção — pagamentos
x402 reais na Base, liquidados on-chain, com os recibos assinados resultantes mostrados exatamente como recebidos.
Ambos são verificáveis de forma independente: busque o cartão do agente acima, confirme que key_version: v1-2026-09c
está listado e verifique cada attestation.signature com os passos acima.
1. Fatura de rascunho — falha em uma regra entre campos (detail: "full")
Solicitação:
{
"task_id": "invoice-7731-draft",
"agent_id": "test-seller-invoice-agent",
"expected_schema": {
"type": "object",
"required": ["invoice_id", "currency", "line_items", "total"],
"properties": {
"invoice_id": { "type": "string", "pattern": "^INV-[0-9]{4}$" },
"currency": { "enum": ["USD", "EUR"] },
"line_items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["sku", "qty", "line_total"],
"properties": {
"sku": { "type": "string" },
"qty": { "type": "integer", "minimum": 1 },
"line_total": { "type": "number", "minimum": 0 }
}
}
},
"total": { "type": "number" }
}
},
"submitted_output": {
"invoice_id": "INV-7731",
"currency": "USD",
"line_items": [
{ "sku": "CRWL-100", "qty": 3, "line_total": 87.0 },
{ "sku": "CRWL-200", "qty": 1, "line_total": 42.5 }
],
"total": 135.0
},
"rules": [
{ "type": "sum_equals", "field": "line_items[].line_total", "equals_field": "total" }
],
"enforce_rules": true,
"detail": "full"
}
Os itens de linha somam 129,5, mas total indica 135,0. Com enforce_rules: true, essa violação de regra
reprova o resultado e vem acompanhada de uma dica de correção:
{
"summary": {
"result": "fail",
"blocking_checks": ["consistency:sum_equals"]
},
"next_actions": [
{ "action": "repair_and_reverify", "using": "hints" }
],
"result": "fail",
"verification_id": "28afb453-7c39-4993-a9c4-fcade1b7924f",
"task_id": "invoice-7731-draft",
"agent_id": "test-seller-invoice-agent",
"output_hash": "d4fb804e434eb3ee047068cbb3691ea1e07066928e7e3efc73b94317e53ee305",
"schema_hash": "23ea3213715cfaf2011dabe2a9a69d4ac25b6eceec7ce3d4bfef9a1298b69b2a",
"rules_hash": "2acb6de92b88cfbca279ea6d5bd74b7a1e7dec0839dd093dbe041e25df3364d2",
"request_hash": "e4bf9733726b24a19ec546ecc79015c9b1cb7a6829b409b41ef56b1c8f60b881",
"verified_at": "2026-10-01T01:01:09.606993+00:00",
"verifier_version": "0.4.1",
"enforce_rules": true,
"strict_content_check": false,
"errors": [
"enforce_rules: consistency:sum_equals at 'line_items[].line_total': values sum to 129.5, expected total (135.0)"
],
"errors_total": 1,
"errors_omitted": 0,
"errors_detail": [
{
"check": "consistency:sum_equals",
"path": "$.line_items[].line_total",
"expected": "a sum equal to total (135.0)",
"found": "sum 129.5",
"message": "enforce_rules: consistency:sum_equals at 'line_items[].line_total': values sum to 129.5, expected total (135.0)"
}
],
"errors_detail_total": 1,
"errors_detail_omitted": 0,
"flags": [
{
"check": "consistency:sum_equals",
"field": "line_items[].line_total",
"severity": "error",
"detail": "values sum to 129.5, expected total (135.0)"
}
],
"flags_total": 1,
"flags_omitted": 0,
"hints": [
{
"source": "integrity",
"check": "consistency:sum_equals",
"path": "$.line_items[].line_total",
"expected": "a sum equal to total (135.0)",
"found": "sum 129.5"
}
],
"hints_total": 1,
"hints_omitted": 0,
"schema_compliance": { "ratio": 1.0, "passed": 22, "total": 22 },
"integrity_checks": { "ratio": 0.875, "passed": 7, "total": 8 },
"overall": { "ratio": 0.966667, "passed": 29, "total": 30 },
"verify": {
"public_key_url": "https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json",
"key_version": "v1-2026-09c",
"canonicalization": "RFC 8785 (JCS)",
"hash_algorithm": "SHA-256",
"service": "Agent Output Verifier",
"mcp": "https://fastapi-service-5ag4.onrender.com/mcp"
},
"attestation": {
"algorithm": "Ed25519",
"key_version": "v1-2026-09c",
"timestamp": "2026-10-01T01:01:09.606993+00:00",
"signature": "8UexIObSAwQxjj8/rbEpLRgbYY6XO+wg5+b0D8I3BRfbOxl+ouwcKNPE/hp+9p37Tm2wwdmCqPjBgOMLGQtGAQ=="
}
}
Comprovante de pagamento: US$ 0,02 USDC na Base, liquidado on-chain —
0x4cb755c71413ed5419215d2d7445c34e19be79d9f15b4046802656165b774897.
2. Fatura corrigida — aprovada (detail: "compact")
Mesmo esquema e regra, total corrigido para corresponder aos itens de linha (129,5), solicitado com
"detail": "compact" para obter apenas o resumo, os campos de recibo e a assinatura:
{
"task_id": "invoice-7731-final",
"agent_id": "test-seller-invoice-agent",
"expected_schema": { "...": "same schema as above" },
"submitted_output": {
"invoice_id": "INV-7731",
"currency": "USD",
"line_items": [
{ "sku": "CRWL-100", "qty": 3, "line_total": 87.0 },
{ "sku": "CRWL-200", "qty": 1, "line_total": 42.5 }
],
"total": 129.5
},
"rules": [
{ "type": "sum_equals", "field": "line_items[].line_total", "equals_field": "total" }
],
"enforce_rules": true,
"detail": "compact"
}
{
"summary": {
"result": "pass",
"blocking_checks": []
},
"next_actions": [
{ "action": "attach_receipt", "for": "seller" },
{ "action": "proceed_to_payment", "for": "buyer" }
],
"result": "pass",
"verification_id": "96b4f722-04c3-47c7-8708-5bba97fd18b5",
"task_id": "invoice-7731-final",
"agent_id": "test-seller-invoice-agent",
"output_hash": "39e3d0a1431e3ce9cda89a1d2873e1ffb4e34f4eeded2ffa87946fbd941a34c3",
"schema_hash": "23ea3213715cfaf2011dabe2a9a69d4ac25b6eceec7ce3d4bfef9a1298b69b2a",
"rules_hash": "2acb6de92b88cfbca279ea6d5bd74b7a1e7dec0839dd093dbe041e25df3364d2",
"verified_at": "2026-10-01T01:01:15.158857+00:00",
"verifier_version": "0.4.1",
"enforce_rules": true,
"strict_content_check": false,
"verify": {
"public_key_url": "https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json",
"key_version": "v1-2026-09c",
"canonicalization": "RFC 8785 (JCS)",
"hash_algorithm": "SHA-256",
"service": "Agent Output Verifier",
"mcp": "https://fastapi-service-5ag4.onrender.com/mcp"
},
"attestation": {
"algorithm": "Ed25519",
"key_version": "v1-2026-09c",
"timestamp": "2026-10-01T01:01:15.158857+00:00",
"signature": "ri1AEl12DoZMTKBX8V5hfuD9jOWd+BLXIciqiX1JoSHJy8iDJKKSdsJmjpQUHTkbVMxDGpNwMNmc6X9zHaSVDw=="
}
}
O formato compacto mantém o resumo, os campos de recibo, verify e attestation; request_hash,
errors, errors_detail, flags, hints e as três pontuações permanecem no formato completo (veja
detail acima).
Comprovante de pagamento: US$ 0,02 USDC na Base, liquidado on-chain —
0xaa9161736ac410b6ac9ccc478780aa2feaa6d2cf02af4c51855e099c14499079.
Verificando esses dois recibos você mesmo
Ambas as respostas acima foram verificadas em relação à chave pública do agent-card ativo para v1-2026-09c
antes de serem incluídas aqui, usando as etapas em Verificando um recibo.
Confirme você mesmo da mesma forma: busque
https://fastapi-service-5ag4.onrender.com/.well-known/agent-card.json, confirme que v1-2026-09c está
listado e execute novamente as mesmas cinco etapas nos dois blocos JSON acima — as assinaturas foram
calculadas uma única vez, exatamente sobre estes dados, e serão verificadas de forma idêntica por qualquer pessoa.
agent_id identifica o agente que produziu a saída que está sendo verificada — por exemplo, o vendedor;
é um rótulo não autenticado fornecido pelo chamador. agent_id: "test-seller-invoice-agent" usa o
prefixo test- que o serviço reserva para exemplos: a verificação foi executada de verdade, e seu recibo é
genuíno, permanecendo separado da pontuação de confiança desse agente.
Licença
A documentação e os exemplos neste repositório são licenciados sob a Licença MIT. O serviço hospedado é proprietário; este repositório contém apenas documentação e metadados públicos.