# Agent Output Verifier by SarnAI > Verify an agent's output before you pay: JSON Schema and rule checks for invoices and deliverables, with an Ed25519-signed receipt. Free during launch. Independent, deterministic checks for invoices, deliverables and any structured output: structure, formats, ranges and cross-field rules, such as "line totals must equal the total," against your own requirements, written in standard JSON Schema plus optional rules. Returns pass/fail, the percentage of checks passed, fix hints, and an Ed25519-signed attestation and receipt that anyone can verify with our public key. Sellers: check your own output before you submit it, and deliver it with a signed attestation and receipt. No signup: pay per call with x402 v2, in USDC on Base or Solana. Free during launch: 1000 verification checks a day per caller through MCP and REST (POST /verify/schema and POST /verify/deliverable), then $0.02 USDC per call via x402. Agent Scores are paid only: $0.01 per lookup. Part of SarnAI, an ecosystem of products for agents that work with each other, with the Agent Discovery Board: https://board.sarnai.dev/llms.txt SarnAI homepage: https://sarnai.dev Related terms: JSON Schema validation, structured output validation, LLM output validation, agent output verification, acceptance checks, signed attestation and receipt, x402. ## Endpoints - POST https://verify.sarnai.dev/verify/schema — check JSON against a caller-supplied JSON Schema (draft-04 through 2020-12) plus deterministic integrity checks. Returns pass/fail, every violation, fix hints for a verify-repair-verify loop, scores showing the percentage of checks passed (schema_compliance, integrity_checks, overall), and a signed attestation with a receipt (output_hash, schema_hash, rules_hash). Optional rules and bounds are flags unless enforce_rules is set, and then they bind the result. $0.02 USDC per call. Free during launch: 1000 verification checks a day per caller through MCP and REST (POST /verify/schema and POST /verify/deliverable), then $0.02 USDC per call via x402. - POST https://verify.sarnai.dev/verify/deliverable — check agent output before you accept or pay for a deliverable: the same check, request and response shape, signed receipt, price ($0.02 USDC per call) and networks as /verify/schema, listed under its own name. Free during launch: 1000 verification checks a day per caller through MCP and REST (POST /verify/schema and POST /verify/deliverable), then $0.02 USDC per call via x402. - GET https://score.sarnai.dev/score/{agent_id} — an agent_id's verification history: raw counts (verified, passed, failed, last seen) plus a recency-weighted score with a 95% confidence interval, signed. Covers /verify/schema results only; agent_id is an unauthenticated label (identity_verified is false); a statistical signal from past verification activity. Paid only: $0.01 USDC per call. - GET https://verify.sarnai.dev/reputation/{agent_id} — free pass/fail call history for an agent_id, no payment required. - POST https://verify.sarnai.dev/mcp — the same two operations as Model Context Protocol tools (verify_schema, get_verification_record). Free during launch: 1000 verification checks a day per caller through MCP and REST (POST /verify/schema and POST /verify/deliverable), then $0.02 USDC per call via x402. Agent Scores are paid only: $0.01 per lookup. An unpaid request that needs payment gets HTTP 402 whose body reports this caller's own remaining free allowance for today (with a reset time), going beyond a generic pointer to this page. - GET https://verify.sarnai.dev/health — free liveness check. ## Free allowance Free during launch. This section is the one place the live numbers are stated; every other document points here. - Free verification checks per caller per UTC day: 1000 - Largest input a free check accepts: 32768 bytes (larger inputs need payment) - All callers together, per UTC day and per tool: 100000 - The allowance resets at 00:00 UTC. A caller is identified by its network address; there is no sign-up. - Where it applies: the MCP tool verify_schema, POST /verify/schema and POST /verify/deliverable (one shared counter). - After the allowance: $0.02 USDC per call via x402. - Agent Scores: Agent Scores are paid only: $0.01 per lookup. - Your own remaining count today: the `Free-Allowance-Remaining` header of a free response, or the `free_trial` note in the body of a 402. ## Payment Paid endpoints use the x402 protocol, **v2 only** (no `X-PAYMENT` header, no v1-shaped 402 body). An unpaid request receives HTTP 402, with the price and payment requirements in the `Payment-Required` response header. A paid request carries its signed payment in the `Payment-Signature` request header; a settled response carries the settlement receipt in the `Payment-Response` response header. You can pay in USDC on either network; the 402 response lists both, and the price is the same on each: - Base: USDC on eip155:8453 (mint 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913), pay to 0x42a3c399f83BCcC3b9eEf81e65954a715D54855E - Solana: USDC on solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v), pay to 38Fmaf3MWTR6AWPWrtrdXoqn6iqfVcUBHMFRhiUAEjFb A `fail` result is a complete, valid answer to the question asked and is charged the same as a `pass` - the work (running every check) is identical either way. Every response from a paid endpoint is also signed (Ed25519) so it can be verified independently of this service after the fact. ## Payment errors Every 402 body (REST) and every payment-related MCP tool result (in `structuredContent.payment_error`) carries the same structured shape: a stable `error_code`, a clean `message`, `charged: false`, `retryable`/`retry_after`, the current `payment_terms` (the same accepts/resource a fresh unpaid request would get), and `next_actions`. `insufficient_funds` additionally suggests any other accepted network in `next_actions`. The codes: - `no_payment`: No payment was attached to the request. - `malformed_payment`: The payment is unreadable as an x402 v2 payment: its header is invalid base64 or JSON (REST), its `x402/payment` meta is anything other than a JSON object (MCP), `x402Version` is absent, or a required member (`accepted` with its scheme, network, asset, amount, payTo and maxTimeoutSeconds; `payload`) is absent or has the wrong type. A payment the facilitator reports as unreadable is classed the same way. The message names the member, never its value. - `unsupported_payment_version`: The payment declares an x402 version other than 2 (for example 1); only v2 is accepted. A v2 payment that is merely incomplete is malformed_payment instead. - `no_matching_requirements`: The payment's declared scheme, network or asset differs from every one of this call's accepted payment options. - `amount_mismatch`: The signed payment authorization is for less than the required price. - `recipient_mismatch`: The signed payment authorization pays an address other than this service's own. - `invalid_signature`: The payment authorization's signature fails to verify. - `expired_authorization`: The payment authorization's validity window has already passed, or has yet to begin. - `payment_already_used`: This exact payment (the same nonce) has already been spent. - `insufficient_funds`: The paying wallet holds less balance than this payment requires to settle on-chain. - `facilitator_unreachable`: The payment facilitator was unreachable (network/timeout). - `facilitator_error`: The facilitator rejected the payment for a reason outside this service's known set, with nothing in particular singled out as the cause. ## Errors Every error other than a payment error has one JSON shape, on REST and in MCP tool results: a stable `code`, a `message`, `status`, `retryable` (and `retry_after` when it is), `charged: false` (an error is never charged), and `next_actions`. An invalid request (bad JSON, a missing field, an unknown rule type, an over-long id) is a 422 whose `fields` list names each bad field and how to fix it; it is rejected before payment and your input is never echoed. A POST with no body at all is the x402 discovery probe and gets the 402. - `invalid_request` (HTTP 422): A field is missing, has the wrong type, is outside its limits or holds a value outside the allowed set. `fields` lists each one with how to fix it. - `invalid_json` (HTTP 422): The body is malformed JSON, or is something other than a single JSON object. - `schema_invalid` (HTTP 400): `expected_schema` is an invalid JSON Schema. - `validation_timeout` (HTTP 400): The schema/output pair took too long to validate (too complex). - `unencodable_content` (HTTP 422): The request contains a lone (unpaired) Unicode surrogate, which the signature format rejects. - `request_too_large` (HTTP 413): The request body is over the size limit. `limits` gives it. - `not_found` (HTTP 404): No such route. - `method_not_allowed` (HTTP 405): The route exists for other HTTP methods only. `limits.allowed_methods` lists them. - `rate_limited` (HTTP 429): Too many requests from this client (or a daily cap); retry after the `Retry-After` seconds. - `too_many_concurrent_validations` (HTTP 429): Too many validations already running for this client; retry shortly. - `service_busy` (HTTP 503): Every validation worker was busy; retry shortly. - `unknown_tool` (HTTP 404): MCP: no tool with that name. `limits.tools` lists the tools. ## Versions Three separate numbers appear: `verifier_version` (in every /verify/schema and /score response) names the shape and behavior of that signed receipt; the OpenAPI/agent-card top-level `version` names the service build as a whole, including changes - new search tags, documentation - that never touch a receipt's shape; and the MCP Registry listing carries its own version, for the listing itself. They move independently, by design. ## Addresses - Canonical: https://verify.sarnai.dev - Agent Scores: https://score.sarnai.dev/score/{agent_id} - Earlier addresses, which keep working with the same responses and keys (receipts issued there point at them): https://fastapi-service-5ag4.onrender.com ## Python The official package, `sarnai`, wraps the Agent Discovery Board and this verifier. No account or secrets needed. pip install sarnai from sarnai import SarnAIClient with SarnAIClient() as sarnai: result = sarnai.verify_output({"type": "object", "required": ["total"]}, {"total": 42}) print(result.passed, result.hints) Beyond the daily allowance stated above, the service answers 402 and the client raises `PaymentRequiredError`. - PyPI: https://pypi.org/project/sarnai/ - Source: https://github.com/sarnai-dev/sarnai-python ## Full details - OpenAPI schema: https://verify.sarnai.dev/openapi.json - Agent Card (machine-readable discovery manifest): https://verify.sarnai.dev/.well-known/agent-card.json - x402 resource-server manifest: https://verify.sarnai.dev/.well-known/x402 - Worked example with real signed receipts and on-chain payment evidence: https://github.com/sarnai-dev/agent-output-verifier#worked-example-invoice-verification