SmartFlow Observatory · smartflowproai.com · info@smartflowproai.com
This is a real report structure with the customer removed. Host names, wallet addresses, transaction hashes and timing figures have been replaced with placeholders. Field names, check names, severity classes and the priority scheme are exactly what you receive. Nothing here is a testimonial and no customer is identified. If you want to see the tool that produces the machine readable half of this document, it is public and free: https://github.com/smartflowproai-lang/x402-endpoint-validator
AUD-0000-EXAMPLEhttps://api.example-operator.tld/v1/report (1 protected route)0x0000...0000 (399 USDC, Base)x402-endpoint-validator 1.4.0, strict_v2: true, probe_method: auto
The route is reachable, answers 402 to an unauthenticated probe, and stays inside the
declared latency budget. It is not currently in a state where a CDP Bazaar style discovery
flow can catalogue it, and one field disagreement means a careful buyer comparing discovery
against the live quote has a reason to stop before signing.
Four defects are listed below: 1 blocking, 2 important, 1 minor.
The blocking defect is P1-01. Nothing else on this list matters until that one is closed.
This verdict describes what the endpoint returned during the probe window above. It is not a statement about future runs, about settlement, or about demand.
Five automated layers, run against the live deployed URL:
402 via GET, POST answered 405)/.well-known/x402 manifestschema_kind: v2-resource-accepts200, no 401, no 403Four manual checks, which the open source validator does not perform:
info validated against the endpoint's own schemapayTo compared across manifest, live challenge and prior runsThe manual checks are the reason this is a paid audit rather than a free CI run. The validator
confirms that a Bazaar block is structurally present. It does not confirm that the block's own schema
accepts the block's own advertised info, which is the gate a facilitator applies before
cataloguing.
Priority means: what will stop a machine buyer first.
schema rejects the endpoint's own info (method enum)Where: live 402 response, extensions.bazaar
in the decoded PaymentRequired object
The route accepts POST with a JSON body. The advertised info.input
correctly declares:
{"type": "http", "method": "POST", "bodyType": "json", "body": {"query": "example"}}
The schema shipped in the same block declares:
"method": {"type": "string", "enum": ["GET", "HEAD", "DELETE"]}
Validating info against schema (JSON Schema Draft 2020-12) produces one
error:
$.input.method: 'POST' is not one of ['GET', 'HEAD', 'DELETE']
A facilitator validates info against schema before it catalogues the
resource. A block that fails its own schema is rejected at that gate. The rejection is silent:
nothing is returned to the operator, the endpoint keeps serving 402 normally, and the
only visible symptom is that the resource never appears in the catalogue.
The open source validator reports bazaar_ok: true for this block, because it checks
that the required keys are present and non-empty strings. It does not run the info
against schema validation. A green CI run does not clear this defect.
pip install jsonschema >/dev/null
python3 - /tmp/payment-required.json <<'PY'
import json, sys
from jsonschema import Draft202012Validator
block = json.load(open(sys.argv[1]))["extensions"]["bazaar"]
Draft202012Validator.check_schema(block["schema"])
for e in Draft202012Validator(block["schema"]).iter_errors(block["info"]):
print("$." + ".".join(str(p) for p in e.path) + ":", e.message)
PY
Replace the method enum with the body-method variant and require the body fields the specification requires for body methods:
"method": {"type": "string", "enum": ["POST", "PUT", "PATCH"]},
"bodyType": {"type": "string", "enum": ["json", "form-data", "text"]},
"body": {"type": "object", "required": ["query"]}
and change input.required to ["type", "method", "bodyType", "body"].
Re-run the reproduction snippet above. Zero errors printed. The re-test included in this audit does exactly this, plus a fresh live probe.
payTo in the manifest disagrees with payTo in the live challengeWhere: /.well-known/x402 versus the live
402 challenge
/.well-known/x4020xAAAA...1111402 challenge0xBBBB...22220xBBBB...2222 (stable)Both are valid addresses and both are checksummed. They are not the same address.
A buyer that compares discovery metadata against the live quote before signing sees two different recipients for one resource. That is the shape of a compromised or misconfigured deployment, and a spending policy is entitled to treat it as one. The live challenge is the authoritative source, so a buyer that never reads your manifest will pay normally. A buyer that does read it has a reason to stop.
Pick one internal source of truth for the recipient address and render both surfaces from it. If the recipient changed deliberately, update the manifest in the same deploy, and publish the change with an effective date.
amount fieldWhere: accepts[0].amount
Advertised price is 0.01 USDC. The challenge carries:
"amount": "0.01"
USDC on Base (0x8335...2913) reports decimals() = 6, verified on chain
during this audit. The atomic value for 0.01 USDC is therefore 10000.
amount is a digit string in atomic units. "0.01" is not a digit string.
A strict parser rejects the quote. A lenient parser that coerces to an integer reads 0,
which is a quote for nothing and will not settle. Neither outcome produces an error message you can
see from your side.
"amount": "10000". Derive it as round(price * 10 ** decimals) at build
time, not by hand, and take decimals from the asset contract rather than from an
assumption.
description on two of three listed resourcesWhere: /.well-known/x402, resources[1]
and resources[2]
Both entries carry a URL, a method and terms, and no description.
No known buyer rejects on this. It is a selection input, not a gate: where a buyer ranks candidate
resources, an entry that states what the caller receives is easier to match against a task than one
that does not. Note that /.well-known/x402 is a community convention, not part of the
specification, so this defect is optional to fix and is scored accordingly.
One sentence per resource, describing what the buyer receives rather than what the product is.
These were checked and found conformant during the probe window. They are listed so you know what the audit covered, not as a guarantee about future runs.
GET returns HTTP 402, not 200,
401 or 403.PAYMENT-REQUIRED header is present and decodes to a
PaymentRequired object. The channel is header canonical, not body only.x402Version is the integer 2.accepts[] is non empty and the exact scheme is present.network is a CAIP-2 identifier.asset, payTo and maxTimeoutSeconds are all populated.resource echoes the requested URL.extra.name and extra.version are present, as exact on EVM
requires for EIP-3009. Values match name() and version() read from the
asset contract.AUD-0000-EXAMPLE.pdfAUD-0000-EXAMPLE-validator.jsonstrict_v2: trueAUD-0000-EXAMPLE-raw/The raw archive is included so that every verdict above can be reproduced from evidence rather than
taken on trust. If you disagree with a finding, the response it was derived from is in the archive with
its probed_at_utc timestamp.
What this audit is: a measurement of what one endpoint returned to public probes during the window stated in the header, and a prioritised list of the defects found.
A clean report proves what your endpoint returned during the probe window. It does not prove that anyone wants what you are selling.
Third party specifications, facilitators and catalogues change. Findings are dated for that reason.
One re-test of the same route is included, free, if requested within 14 days of the report date above. It repeats every check in section 2 and returns a short delta document: which findings are closed, which are still open, and any new finding introduced by the fix.
To use it, email info@smartflowproai.com with this report ID.