Anonymised example report

x402 Endpoint Conformance
and Reliability Audit

SmartFlow Observatory · smartflowproai.com · info@smartflowproai.com

Read this first

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

Field · Value
Report ID
AUD-0000-EXAMPLE
Subject
https://api.example-operator.tld/v1/report (1 protected route)
Requested by
(customer name withheld in this example)
Payment confirmed
0x0000...0000 (399 USDC, Base)
Probes executed
2026-08-DD 09:14 UTC to 2026-08-DD 09:41 UTC
Validator
x402-endpoint-validator 1.4.0, strict_v2: true, probe_method: auto
Report issued
2026-08-DD
Re-test entitlement
one free re-test of the same route, expires 2026-09-DD
Section 1

Verdict

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.

Section 2

What was checked

Five automated layers, run against the live deployed URL:

Layer · Result
Reachability
pass (HTTP 402 via GET, POST answered 405)
/.well-known/x402 manifest
pass, host level, schema_kind: v2-resource-accepts
402 conformance (v2 header channel)
fail, see P1-01
Response time p50 / p95 / p99
pass, below the 1000 ms budget
Payment required behaviour
pass, no silent 200, no 401, no 403

Four manual checks, which the open source validator does not perform:

Check · Result
Bazaar info validated against the endpoint's own schema
fail, see P1-01
payTo compared across manifest, live challenge and prior runs
fail, see P2-01
Repeat probe drift on fixed terms (5 probes, 20 minute spread)
pass, terms identical
Amount unit check, display price against atomic value
fail, see P2-02

The 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.

Section 3

Defect list, by priority

Priority means: what will stop a machine buyer first.

P1-01 · Bazaar schema rejects the endpoint's own info (method enum)

Severity: blocking

Where: live 402 response, extensions.bazaar in the decoded PaymentRequired object

Observed

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']

Why it matters

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.

Reproduce it yourself

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

Fix

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"].

How you prove it is closed

Re-run the reproduction snippet above. Zero errors printed. The re-test included in this audit does exactly this, plus a fresh live probe.

P2-01 · payTo in the manifest disagrees with payTo in the live challenge

Severity: important

Where: /.well-known/x402 versus the live 402 challenge

Observed

Source · payTo
/.well-known/x402
0xAAAA...1111
live 402 challenge
0xBBBB...2222
challenge, probed again 20 minutes later
0xBBBB...2222 (stable)

Both are valid addresses and both are checksummed. They are not the same address.

Why it matters

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.

Fix

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.

P2-02 · Display price copied into the atomic amount field

Severity: important

Where: accepts[0].amount

Observed

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.

Why it matters

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.

Fix

"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.

P3-01 · Manifest omits description on two of three listed resources

Severity: minor

Where: /.well-known/x402, resources[1] and resources[2]

Observed

Both entries carry a URL, a method and terms, and no description.

Why it matters

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.

Fix

One sentence per resource, describing what the buyer receives rather than what the product is.

Section 4

What passed, stated explicitly

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.

Section 5

Attachments delivered with this report

File · What it is
AUD-0000-EXAMPLE.pdf
this document
AUD-0000-EXAMPLE-validator.json
full validator output, machine readable, strict_v2: true
AUD-0000-EXAMPLE-raw/
archived raw responses, all headers included, one file per probe

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.

Section 6

Scope and limits

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.

What it is not
  • not a security assessment, a penetration test or a code review
  • not a smart contract audit
  • not a guarantee of catalogue indexation, of discovery, or of any payment
  • not a statement about revenue or demand
  • not a statement about uptime, settlement correctness or post payment behaviour
  • not a certification, and it does not issue a badge

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.

Section 7

Your re-test

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.