Black-box conformance and security auditing for x402 HTTP endpoints.
x402-audit is a Rust CLI that tests whether an HTTP endpoint actually behaves
correctly as an x402 payment-protected resource.
It validates the wire protocol, payment requirements, discovery metadata, rejection behavior, and basic payment-gating invariants — without requiring you to modify the application being tested, and without ever spending money.
cargo install x402-audit
x402-audit https://api.example.com/paid/weatherx402-audit
https://api.example.com/paid/weather 0.1.0
HTTP
✓ endpoint reachable
✓ returns 402 Payment Required without payment
✓ PAYMENT-REQUIRED header present
✓ PAYMENT-REQUIRED header decodes as base64
...
Protocol
✓ x402Version = 2 (v2)
✓ network `eip155:84532` is a valid CAIP-2 identifier
✓ payTo is a valid EVM address
✗ amount `not-a-number` is not a valid non-negative decimal (atomic units)
[REQ_AMOUNT_VALID]
Discovery
✓ bazaar extension present
✗ bazaar extension must be an object with `info` (object) and `schema` (object)
[BAZAAR_SHAPE_VALID]
Payment gate
✓ endpoint rejected a payment payload whose accepted.amount does not match (HTTP 400)
✗ endpoint returned 200 OK and released the protected resource for a malformed PAYMENT-SIGNATURE
[PAYMENT_MALFORMED_B64_REJECTED]
Result: FAIL
3 errors, 1 warning
An endpoint can appear to support x402 while still having subtle implementation problems:
- malformed
402 Payment Requiredresponses - invalid or incomplete payment requirements
- incorrect network or asset identifiers
- inconsistent resource metadata
- malformed Bazaar extensions (
extensions.bazaar.bazaar, missinginput.method, …) - missing HTTP headers
- accepting malformed payment payloads
- failing to reject invalid payment proofs
- payment-gating behavior that does not match the advertised requirements
These problems are particularly difficult to diagnose because the failure may occur across several layers:
HTTP
↓
x402 wire format
↓
payment requirements
↓
payment authorization
↓
facilitator verification
↓
settlement
↓
resource delivery
x402-audit turns those layers into an automated test suite. Existing
validators answer "does this JSON look correct?"; x402-audit additionally
answers "does this HTTP endpoint behave like a correct x402 resource?" by
exercising the payment gate with safely-invalid payloads.
Requires Rust 1.75+ (no system dependencies — HTTP is built on rustls).
cargo install x402-audit
# or build from source
cargo build --releasex402-audit URL # human-readable report
x402-audit URL --json # machine-readable (CI)
x402-audit URL --github-summary # GitHub Actions job summary
x402-audit URL --strict # warnings count as failures
x402-audit URL --exit-code # exit 0/1/2 (see below)
x402-audit URL --method POST --body '{"q":"weather"}' -H "X-Key: secret"
x402-audit --discover https://api.example.com # audit every listed resource
x402-audit --discover --limit 50 URL # cap the number of resources
x402-audit --discover --json URL > x402-discover.json # aggregated JSON (CI)| Flag | Meaning |
|---|---|
--json |
Stable, machine-readable JSON report on stdout |
--github-summary |
Markdown job-summary for GitHub Actions |
--strict |
Treat warnings as failures |
--exit-code |
Exit 0 = PASS, 1 = FAIL, 2 = UNREACHABLE |
--quiet |
Only failures and the summary |
--discover |
Treat the URL as a discovery origin or .well-known/x402 manifest and audit every listed resource |
--limit N |
Max resources audited per discovery run (default 25, 0 = unlimited) |
-H/--header, --body, --method, --timeout |
Request configuration |
--discover fetches the machine-readable manifest at /.well-known/x402
(falling back to /.well-known/x402.json) and audits every listed resource in
one run — ideal for CI gates over an entire API surface. The parser is
shape-tolerant: it accepts the manifest formats observed in production
(resources as URL strings or objects, endpoints with relative paths,
resource-groups), resolves relative paths against the manifest origin,
deduplicates, and respects --limit. A manifest that cannot be fetched or
parsed is an UNREACHABLE result (exit 2); any failing resource makes the
whole run FAIL (exit 1).
Every finding has a stable identifier, severity, status, and message — perfect for GitHub Actions later:
{
"tool": "x402-audit",
"target": "https://api.example.com/paid/weather",
"result": "FAIL",
"summary": { "passed": 40, "failed": 3, "warnings": 1, "skipped": 0 },
"findings": [
{
"id": "PAYMENT_MALFORMED_B64_REJECTED",
"group": "payment_gate",
"severity": "error",
"status": "fail",
"message": "endpoint returned 200 OK and released the protected resource for a malformed (non-base64) PAYMENT-SIGNATURE header"
}
]
}x402-audit https://api.example.com/paid/weather \
--json --exit-code > x402-report.jsonSee .github/workflows/ci.yml for a complete
workflow, including publishing the report as an artifact.
The crate ships a bundled fixture server with a correct implementation and several intentionally broken ones:
# Terminal 1 — start a broken endpoint (or `manifest` for discovery mode)
cargo run --example fixture-server --features fixtures manifest
# Terminal 2 — audit it (discovery mode audits every endpoint at once)
cargo run -- x402-audit --discover http://127.0.0.1:PORT
# or, for a single endpoint:
cargo run -- x402-audit http://127.0.0.1:PORT/weatherFixture kinds: correct, v1, broken-protocol, broken-bazaar,
broken-requirements, leaky-gate, no-gate, manifest.
cargo test --all-features # proves the auditor catches each broken fixture| Fixture | The auditor reports |
|---|---|
correct |
PASS (48 checks) |
v1 |
warning + v2 checks skipped (graceful degradation) |
broken-protocol |
RESOURCE_URL_VALID, REQ_ACCEPTS_REQUIRED_FIELDS |
broken-bazaar |
BAZAAR_SHAPE_VALID (nested-key bug) |
broken-requirements |
REQ_NETWORK_EIP155, REQ_ASSET_VALID, REQ_PAYTO_VALID, REQ_AMOUNT_VALID |
leaky-gate |
all 10 payment-gate checks (PAYMENT_*_REJECTED) |
no-gate |
HTTP_402_RETURNED, HTTP_HEADER_PRESENT |
manifest |
one server, three endpoints + a .well-known/x402 manifest; --discover finds them all |
Live audits of real public x402 endpoints (no payments made — the payment-gate tests only send structurally-invalid payloads):
| Endpoint | Result | Findings |
|---|---|---|
kaisha-api.hp-vladic.workers.dev/resolve (Base + Solana) |
PASS | 44 checks, 0 errors |
langston.click/api/search (Solana, USDC) |
FAIL | BAZAAR_METHOD_PRESENT — bazaar.info.input.method missing, facilitators can't call it |
api.octodamus.com/v2/agent-signal (Base, USDC) |
FAIL | RESOURCE_URL_VALID — resource is null; v2 requires a ResourceInfo object |
agent402.tools/api/dns (13 accepts, 12 networks) |
FAIL | REQ_NETWORK_CAIP2 — algorand:…kit8= is the full base64 Algorand MainNet genesis hash; the reference must be its first 32 base64url chars (registry-resolved hint) |
All four endpoints correctly rejected every invalid payment payload (no leaky
gates found). This run also surfaced a real validator bug — CAIP-2 references
may contain _, which x402-audit now accepts (see parse_caip2).
Networks are now resolved against a bundled ChainAgnostic registry snapshot
(data/networks.json), so audits name chains (eip155:8453 → Base) and
recognize malformed references like untruncated genesis hashes — see
docs/checks.md for provenance and refresh instructions.
--discover audits whole manifests in one run and finds what single-URL
audits can't: the kaisha manifest surfaced /catalog (listed, but not
payment-gated: HTTP_402_RETURNED, HTTP_HEADER_PRESENT), and two of
agent402's advertised URLs (/api/extract, /api/render) turned out to be
dead 404s.
The audit runs four groups of checks:
- HTTP — reachability, 402 semantics,
PAYMENT-REQUIREDheader, base64/JSON decoding, content type. - Protocol — x402 v2
PaymentRequiredenvelope: version,resourcemetadata, and everyaccepts[]entry (scheme, CAIP-2 network, asset,payTo, amount,maxTimeoutSeconds,extra), plus header/body consistency. - Discovery — the
bazaarextension: shape, input schema,methodmetadata, advertised JSON-Schema sanity,routeTemplate. - Payment gate — ten safely-invalid
PAYMENT-SIGNATUREpayloads that a conformant endpoint must reject (the protected resource must never be returned).
The complete registry with stable IDs lives in docs/checks.md.
docs/architecture.md— how the tool is built and whydocs/checks.md— the full check registrydocs/methodology.md— where the checks come fromdocs/threat-model.md— what we test and what we deliberately don't
The tool tests the endpoint through HTTP rather than requiring framework-specific integration.
The same endpoint and test configuration produce the same result. Reports contain no timestamps; checks and negative tests run in a fixed order.
The default audit suite never spends real money and never attempts destructive operations. The payment-gate tests only send structurally-invalid payloads that cannot be settled.
Tests correspond to documented x402 behavior and explicit security invariants,
not arbitrary style opinions. Several checks were derived from real ecosystem
integration failures (see docs/methodology.md).
Every finding has a stable identifier, severity, description, and machine-readable representation.
x402-audit is not:
- an x402 SDK
- a facilitator
- a payment processor
- a wallet
- an x402 gateway
- a discovery marketplace
- a blockchain explorer
It is a testing and auditing tool for existing x402 implementations.
- Audits x402 v2 with the
exactscheme and EVM network semantics (Base/Ethereum). Other networks and schemes are reported but not fully audited. - Network resolution uses a bundled ChainAgnostic registry snapshot
(
data/networks.json): known networks are named, unknown ones warn (fail under--strict). Seedocs/checks.mdfor refresh steps. - Does not positively verify that a valid payment is accepted — that would require a wallet and real (or testnet) funds. The positive path is out of scope by design.
- Does not audit facilitator
/verify//settleendpoints.
MIT — see LICENSE.