You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Proposal: a neutral pre-payment recipient hook in the x402 action provider (inspect payTo before paying) #1402
The x402 action provider gates payments on two axes today — host (isUrlAllowed) and amount (validatePaymentLimit) — but nothing lets an operator inspect the payment recipient wallet (payTo) before wrapFetchWithPayment signs in retryWithX402.
The gap. "Is the address my agent is about to send USDC to sanctioned / a known drainer / phishing?" is a distinct axis from host-allowlist and amount-cap, and arguably the one an autonomous payer most wants closed before signing. The selected requirements already carry payTo (it is on PaymentRequirements), so the data is in hand — there is just no decision point exposed to config.
Precedent. The official @x402 core client already exposes onBeforePaymentCreation(context) returning { abort, reason }, with context.selectedRequirements.payTo available. AgentKit wraps @x402/fetch but does not surface an equivalent pre-pay decision point to x402ActionProvider config.
evaluated in retryWithX402right after validatePaymentLimit and before wrapFetchWithPayment — same shape as the existing isUrlAllowed / validatePaymentLimit gates. It just hands the operator the selected requirements and lets them allow/deny. No vendor coupling — the hook is provider-agnostic.
Use case / one implementation. Recipient sanctions + address-reputation screening before an autonomous send. anchor-x402-safe-pay (npm/PyPI) is one drop-in that returns an allow/review/block verdict for a payTo, but the hook itself is generic.
Happy to send the PR (config field + hook eval in retryWithX402 + a test, mirroring validatePaymentLimit) if you are open to the addition — proposing first since it is a new public config surface.
+1 — a neutral pre-payment payTo hook is the natural seat for compliance/AML screening on the buyer side, and the x402 client already ships the abortable onBeforePaymentCreation precedent, so this fits cleanly with existing patterns.
One design note: let the hook express three outcomes, not two. A compliance screen that can't evaluate (upstream down, unknown address) should be able to return UNCERTAIN and let the integrator choose fail-open vs fail-closed, instead of being forced into a false allow. And a hard deny should require strong, verifiable evidence (sanctions / mixer / issuer-freeze), not a soft score crossing a threshold — otherwise the hook and a plain fail-closed default become indistinguishable.
Strong +1 on the three-state contract — from practice, not theory. A recipient screen that can't evaluate (provider down, unknown address, malformed response) has to be able to say UNCERTAIN rather than get coerced into allow — that's precisely how a "fail-closed" guard silently fails open. I shipped a fix to my own screen wrapper for exactly that bug last week, so I'll back this one from experience.
One refinement on the states: "couldn't evaluate" and "evaluated, but elevated risk" are both non-allow but they're different signals, and the integrator should choose the policy for each. In practice that's two knobs: the verdict (allow / review / block) and the on-error behavior (fail-open vs fail-closed on UNCERTAIN). Collapsing them loses the case a lot of teams actually want — hold on review, but let me decide what happens when the screen itself is down.
Two things I'd anchor the design on:
Keep the hook provider-neutral. Its value is that it's a seat, not a vendor. If the callback carries { decision, evidence } and nothing provider-specific, any backend drops in — Ocean's adapters, my anchor-x402 /v1/screen (already returns allow/review/block + per-signal evidence: OFAC + address-reputation), or a team's in-house list. Multiple independent implementations already returning this shape is a good sign it's the right abstraction.
A hard block should require verifiable evidence (sanctions / mixer / issuer-freeze), with softer scores mapping to review/uncertain — otherwise the hook collapses into a plain fail-closed default and adds nothing over the existing onBeforePaymentCreation abort.
Went ahead and pre-built this so there's a concrete diff to react to rather than an open question: #1454.
It adds an optional, provider-neutral beforePayment hook to retry_http_request_with_x402 — runs after amount + network validation and immediately before signing/settlement, receives the payment context (incl. payTo), and { abort: true, reason } refuses the payment without paying. No screening backend is imported; any implementation plugs in. Matches the three-state-friendly shape discussed above (decision + reason, agnostic to how the verdict was reached).
Backward compatible (no behavior change unless the hook is set), +2 tests mirroring the existing retry tests. Scoped to the two-step flow, which exposes payTo pre-payment; the one-shot make_http_request_with_x402 auto-settles inside wrapFetchWithPayment and can't expose the recipient before paying, so it's intentionally not gated (noted in the PR). Happy to add Python parity there if you want it in the same PR.
Would appreciate a read on whether this is the shape you'd merge.
Named implementation note for the optional beforePayment seat this issue proposes (after amount/network validation, before wrapFetchWithPayment signs).
If a Solana x402 client already exposes beforePayment / onBeforePaymentCreation, the drop-in wash-refuse is:
That hook is after requirement select, before sign. { abort: true, reason } on wash; signer_invocation_count stays 0. No vendor lock in AgentKit — same shape as the generic hook in #1454 / this issue. Live pin is npm view twzrd-x402-gate version (0.9.5), not a frozen comment.
Not asking Coinbase to vendor this. One implementation of the recipient axis you already named.
Exactly the intent — the value is a provider-neutral seat, not any one vendor's screen.
#1454 wires this shape into retryWithX402 (after amount/network validation, before wrapFetchWithPayment signs), returning { abort, reason } with the signer never invoked on refuse — and shows anchor-x402-safe-pay's screenAllows (sanctions + address-reputation axis) as one reference implementation. twzrd-x402-gate covering the wash axis is another. That's the point: multiple independent implementations registering on a single neutral hook is precisely the argument for landing the seat in AgentKit rather than any provider re-implementing amount/host gates from scratch.
Another implementation of this pattern, for reference. It targets the same stage as #1454 — after amount and network validation, immediately before the signer runs — and is provider-neutral: it does not require AgentKit to depend on any particular screening provider.
One additional failure mode may be worth considering alongside the recipient axis: the gap between the payment requirements the seller published and the ones the agent ends up paying against.
If the seller signs its payment requirements (for example, Ed25519 over a canonical representation of the resource plus the accepts[] entries), and the seller's verification key is already authenticated or bound to that origin, the hook can verify that signature offline before any recipient screening happens.
A payTo substituted between the seller and the agent would then fail signature verification rather than simply being screened as a different recipient. That distinction matters because a newly introduced address may have little or no adverse history yet, so recipient-reputation screening alone may not detect the substitution.
A few implementation details may also be useful input:
Three outcomes (allow / review / decline) instead of a boolean. A boolean cannot distinguish "screened and clean" from "could not screen", and an autonomous payer needs to treat those differently.
Machine-readable reason codes on every path, including failure paths, so the caller can branch on why rather than parse prose.
In this implementation, undecodable or unavailable states never resolve to allow.
The verdict envelope these packages emit — the three outcomes and reason-code set — is documented at https://oceanalt.com/en/rap. That is our own framework, not an x402 or AgentKit standard; the link is included only to make the field semantics explicit.
This is a good addition — the requirements-integrity axis is genuinely distinct from recipient reputation, and you're right that reputation alone can't catch a payTo substituted in transit (a fresh address has no history to screen against). Signature-verifying the seller's published requirements before screening closes that gap.
The nice part is both checks ride the same insertion point #1454 proposes: after amount/network validation, before the signer runs. The hook is provider-neutral by design — it can carry a signature check, a screening verdict, or both, without AgentKit depending on any one framework. That's the layer I'd like to see land in core; the verdict logic (yours, ours, anyone's) stays pluggable above it.
+1 on the three-outcome envelope and machine-readable reason codes, with undecodable/unavailable never resolving to allow — that's the same contract our /v1/screen returns (allow/review/block, fail-closed), and an autonomous payer really does need to distinguish "screened clean" from "couldn't screen."
Agreed — the seat in core with the verdict logic pluggable above it is the right boundary, and both axes riding the same insertion point is the cleanest framing of it.
Two implementation notes from building against this pattern, offered as input to #1454 rather than as requirements.
1. Verify the requirements from the source they actually travel in.
In x402 v2, PaymentRequired is carried base64-encoded in the PAYMENT-REQUIRED response header. The response body is not the canonical source of the payment requirements and may be empty.
Our first implementation signed a canonical form derived from the body. It passed its own tests and broke on the first live run against a real endpoint, because we were authenticating a representation that carried nothing.
So the signed material should be derived from the PaymentRequired object actually received through that header. Whether an implementation signs the wire representation or a defined canonical subset of the decoded object can stay implementation-specific: a signer over raw bytes needs access to the raw header, while one signing a canonical field set does not.
Core does not need to prescribe either — only to leave the wire form reachable for implementations that need it.
2. One runtime boundary that may be worth making explicit.
A provider can expose richer outcomes such as allow / review / decline, but an adapter ultimately has to map those into the hook contract. If that mapping returns an object AgentKit does not recognize, the current execution path only checks decision.abort; an unrecognized non-empty shape therefore falls through and payment proceeds.
We ran into this from the implementation side: our screening interface emits a three-outcome verdict, so the translation into the hook's execution contract is ours to get right — which is also why the boundary is visible to us.
The invariant worth protecting at the execution boundary seems narrow: an unrecognized return shape should not silently become permission to sign.
The reason to guard it there, rather than relying on every adapter to get the mapping right, is that the failure is silent and fails in the direction of paying. The two tests currently in #1454 cover { abort: true } and the normal allow path, but not an unrecognized non-empty return shape.
Concretely this could stay small: if the hook returns a non-empty value that does not contain a recognized abort decision, return a structured hook-contract error and do not invoke the signer. A test for that case alongside the two already in the PR would make the invariant explicit.
There is a similar information-loss issue with reasons: the current hook exposes a human-readable reason?: string, while providers may already have machine-readable reason codes. Preserving an optional code across the adapter boundary would let autonomous callers branch without parsing prose.
None of this asks core to interpret a provider's verdict logic — that stays pluggable above the hook, as you described. Happy for our implementation to sit behind that interface rather than be part of it.
Both land, and both improve #1454 — thanks for bringing implementation scars rather than opinions.
1. Sign from the header, not the body. Confirmed from the seller side: our v2 402 carries the full PaymentRequired base64 in the PAYMENT-REQUIRED response header, and the body is a separate, often-empty thing — an integrity check derived from the body authenticates nothing, exactly as you hit. So the hook context needs to expose the decoded PaymentRequired object (and the raw wire header, for a bytes-signer) as the canonical source. Agreed core shouldn't prescribe raw-bytes vs canonical-subset — only leave the wire form reachable. I'll make the context surface that, not just the parsed body.
2. An unrecognized return shape must not become permission to sign. Strong +1, and I've hit this exact failure mode: a buyer-side screening wrapper of ours had a fail-open bug where an unknown/missing verdict wasn't in the block set, so it resolved to allow — the fix was to validate the decision is a recognized shape before treating "no abort" as permission, and fail closed on anything else. Same invariant here: if the hook returns a non-empty value without a recognized abort decision, that's a hook-contract error → don't invoke the signer. I'll add that guard plus the third test (unrecognized non-empty return ⇒ no sign) alongside the existing {abort:true} and allow cases.
On reasons: agreed that reason?: string drops machine-readable codes. I'll widen the contract to carry an optional code through the boundary so autonomous callers can branch without parsing prose — provider verdict logic still stays pluggable above the hook.
Appreciate you keeping your impl behind the interface; that's the right boundary.
Fail-closed: a non-empty beforePayment return without a boolean abort is now a hook-contract error — the signer is not invoked. Unrecognized shapes can't fall through to paying anymore.
code passthrough: the decision carries an optional machine-readable code alongside reason, surfaced in the action result.
Requirements reachable:make_http_request now returns the decoded paymentRequirements and the raw paymentRequirementsHeader (v2's authenticated source), and retry_http_request_with_x402 threads both into the hook context — so an integrity check can verify the requirements, not just the recipient.
Tests cover unrecognized-shape ⇒ no sign and the code passthrough (25 pass); README documents the contract. Verdict logic stays pluggable above the hook, as discussed — appreciate the review.
MACH Base is live x402 infrastructure for Base-chain reads with no signup or API key. We are trying to verify the cleanest caller-pays integration path for your agent/runtime. A live compatibility call is GET https://api.mach.gallery/api/base/block-number ($0.005 USDC on Base); discovery: https://api.mach.gallery/.well-known/x402 ; OpenAPI: https://api.mach.gallery/openapi.json . If this fits your stack, one real paid call is enough to validate it. If not, a short note on the blocking integration/policy is useful. MACH conversation=99bd1fe67a59cac772fc7011.
We shipped this as a standalone preflight (ok | caution | avoid plus payTo history) and opened a provider PR that hooks the same spot before wrapFetchWithPayment signs: #1613. Happy to align on a neutral beforePayment shape. Disclosure: x402check is my tool.
Thanks for the note. Happy to align, since a shared shape is the point of #1402.
The neutral piece is #1454: an optional beforePayment on X402Config in the existing x402 action provider. It runs after amount/network validation and just before signing, receives the selected option's payTo (plus the payment requirements), and returning { abort: true, reason } refuses the payment. It has no vendor dependency, so any screener can plug in, including x402check.
Given that, the cleanest alignment might be for the beforePayment helper in #1613 to return the X402BeforePaymentDecision shape from #1454. Then x402check works as a drop-in hook with no second surface. Could you also drop "Closes #1402#1454" from #1613? A vendor action provider is a useful addition, but it doesn't implement the neutral hook itself, and maintainers may want to review the two separately.
Disclosure: I run anchor-x402, which also offers recipient screening.
#1613's createX402checkBeforePayment / create_x402check_before_payment now returns the exact X402BeforePaymentDecision shape from #1454 ({ abort, reason?, code? }), so it drops into beforePayment as a provider-neutral hook. The Closes #1402 #1454 line is removed (now Related: #1402, #1454); happy for maintainers to review the two PRs separately.
Standalone preflight_check / verify_payto actions stay for callers who want an explicit tool. Types are defined locally until #1454 merges (no unmerged imports).
Disclosure: I maintain x402check (endpoint + history preflight: live 402 price/network/payTo vs that URL's history, including payTo-change detection).
Thanks, that's exactly the alignment I was hoping for. With x402check returning the X402BeforePaymentDecision shape, #1454 now has two independent screeners ready to plug into the same beforePayment hook: x402check, and anchor-x402-safe-pay as in its README example.
One more implementation for the proposed beforePayment hook in #1454: @oceanalt/agentkit-hook (v0.1.0) returns its X402BeforePaymentDecision shape, undefined to allow or { abort: true, reason, code } to abort. In the default block mode, decline and review abort the payment, and if client.decide() rejects, the hook treats that as review and aborts as well.
Activity
OceanAlt666 commentedon Aug 17, 2026
+1 — a neutral pre-payment
payTohook is the natural seat for compliance/AML screening on the buyer side, and the x402 client already ships the abortableonBeforePaymentCreationprecedent, so this fits cleanly with existing patterns.One design note: let the hook express three outcomes, not two. A compliance screen that can't evaluate (upstream down, unknown address) should be able to return UNCERTAIN and let the integrator choose fail-open vs fail-closed, instead of being forced into a false
allow. And a harddenyshould require strong, verifiable evidence (sanctions / mixer / issuer-freeze), not a soft score crossing a threshold — otherwise the hook and a plain fail-closed default become indistinguishable.hypeprinter007-stack commentedon Aug 17, 2026
Strong +1 on the three-state contract — from practice, not theory. A recipient screen that can't evaluate (provider down, unknown address, malformed response) has to be able to say UNCERTAIN rather than get coerced into
allow— that's precisely how a "fail-closed" guard silently fails open. I shipped a fix to my own screen wrapper for exactly that bug last week, so I'll back this one from experience.One refinement on the states: "couldn't evaluate" and "evaluated, but elevated risk" are both non-allow but they're different signals, and the integrator should choose the policy for each. In practice that's two knobs: the verdict (
allow/review/block) and the on-error behavior (fail-open vs fail-closed on UNCERTAIN). Collapsing them loses the case a lot of teams actually want — hold on review, but let me decide what happens when the screen itself is down.Two things I'd anchor the design on:
{ decision, evidence }and nothing provider-specific, any backend drops in — Ocean's adapters, my anchor-x402/v1/screen(already returns allow/review/block + per-signal evidence: OFAC + address-reputation), or a team's in-house list. Multiple independent implementations already returning this shape is a good sign it's the right abstraction.blockshould require verifiable evidence (sanctions / mixer / issuer-freeze), with softer scores mapping to review/uncertain — otherwise the hook collapses into a plain fail-closed default and adds nothing over the existingonBeforePaymentCreationabort.hypeprinter007-stack commentedon Aug 18, 2026
Went ahead and pre-built this so there's a concrete diff to react to rather than an open question: #1454.
It adds an optional, provider-neutral
beforePaymenthook toretry_http_request_with_x402— runs after amount + network validation and immediately before signing/settlement, receives the payment context (incl.payTo), and{ abort: true, reason }refuses the payment without paying. No screening backend is imported; any implementation plugs in. Matches the three-state-friendly shape discussed above (decision + reason, agnostic to how the verdict was reached).Backward compatible (no behavior change unless the hook is set), +2 tests mirroring the existing retry tests. Scoped to the two-step flow, which exposes
payTopre-payment; the one-shotmake_http_request_with_x402auto-settles insidewrapFetchWithPaymentand can't expose the recipient before paying, so it's intentionally not gated (noted in the PR). Happy to add Python parity there if you want it in the same PR.Would appreciate a read on whether this is the shape you'd merge.
twzrd-sol commentedon Sep 12, 2026
Named implementation note for the optional
beforePaymentseat this issue proposes (after amount/network validation, beforewrapFetchWithPaymentsigns).If a Solana x402 client already exposes
beforePayment/onBeforePaymentCreation, the drop-in wash-refuse is:That hook is after requirement select, before sign.
{ abort: true, reason }on wash;signer_invocation_countstays 0. No vendor lock in AgentKit — same shape as the generic hook in #1454 / this issue. Live pin isnpm view twzrd-x402-gate version(0.9.5), not a frozen comment.Not asking Coinbase to vendor this. One implementation of the recipient axis you already named.
hypeprinter007-stack commentedon Sep 12, 2026
Exactly the intent — the value is a provider-neutral seat, not any one vendor's screen.
#1454 wires this shape into
retryWithX402(after amount/network validation, beforewrapFetchWithPaymentsigns), returning{ abort, reason }with the signer never invoked on refuse — and shows anchor-x402-safe-pay'sscreenAllows(sanctions + address-reputation axis) as one reference implementation.twzrd-x402-gatecovering the wash axis is another. That's the point: multiple independent implementations registering on a single neutral hook is precisely the argument for landing the seat in AgentKit rather than any provider re-implementing amount/host gates from scratch.OceanAlt666 commentedon Sep 15, 2026
Another implementation of this pattern, for reference. It targets the same stage as #1454 — after amount and network validation, immediately before the signer runs — and is provider-neutral: it does not require AgentKit to depend on any particular screening provider.
One additional failure mode may be worth considering alongside the recipient axis: the gap between the payment requirements the seller published and the ones the agent ends up paying against.
If the seller signs its payment requirements (for example, Ed25519 over a canonical representation of the resource plus the accepts[] entries), and the seller's verification key is already authenticated or bound to that origin, the hook can verify that signature offline before any recipient screening happens.
A payTo substituted between the seller and the agent would then fail signature verification rather than simply being screened as a different recipient. That distinction matters because a newly introduced address may have little or no adverse history yet, so recipient-reputation screening alone may not detect the substitution.
A few implementation details may also be useful input:
The implementation:
The verdict envelope these packages emit — the three outcomes and reason-code set — is documented at https://oceanalt.com/en/rap. That is our own framework, not an x402 or AgentKit standard; the link is included only to make the field semantics explicit.
hypeprinter007-stack commentedon Sep 15, 2026
This is a good addition — the requirements-integrity axis is genuinely distinct from recipient reputation, and you're right that reputation alone can't catch a
payTosubstituted in transit (a fresh address has no history to screen against). Signature-verifying the seller's published requirements before screening closes that gap.The nice part is both checks ride the same insertion point #1454 proposes: after amount/network validation, before the signer runs. The hook is provider-neutral by design — it can carry a signature check, a screening verdict, or both, without AgentKit depending on any one framework. That's the layer I'd like to see land in core; the verdict logic (yours, ours, anyone's) stays pluggable above it.
+1 on the three-outcome envelope and machine-readable reason codes, with undecodable/unavailable never resolving to allow — that's the same contract our
/v1/screenreturns (allow/review/block, fail-closed), and an autonomous payer really does need to distinguish "screened clean" from "couldn't screen."OceanAlt666 commentedon Sep 16, 2026
Agreed — the seat in core with the verdict logic pluggable above it is the right boundary, and both axes riding the same insertion point is the cleanest framing of it.
Two implementation notes from building against this pattern, offered as input to #1454 rather than as requirements.
1. Verify the requirements from the source they actually travel in.
In x402 v2,
PaymentRequiredis carried base64-encoded in thePAYMENT-REQUIREDresponse header. The response body is not the canonical source of the payment requirements and may be empty.Our first implementation signed a canonical form derived from the body. It passed its own tests and broke on the first live run against a real endpoint, because we were authenticating a representation that carried nothing.
So the signed material should be derived from the
PaymentRequiredobject actually received through that header. Whether an implementation signs the wire representation or a defined canonical subset of the decoded object can stay implementation-specific: a signer over raw bytes needs access to the raw header, while one signing a canonical field set does not.Core does not need to prescribe either — only to leave the wire form reachable for implementations that need it.
2. One runtime boundary that may be worth making explicit.
A provider can expose richer outcomes such as allow / review / decline, but an adapter ultimately has to map those into the hook contract. If that mapping returns an object AgentKit does not recognize, the current execution path only checks
decision.abort; an unrecognized non-empty shape therefore falls through and payment proceeds.We ran into this from the implementation side: our screening interface emits a three-outcome verdict, so the translation into the hook's execution contract is ours to get right — which is also why the boundary is visible to us.
The invariant worth protecting at the execution boundary seems narrow: an unrecognized return shape should not silently become permission to sign.
The reason to guard it there, rather than relying on every adapter to get the mapping right, is that the failure is silent and fails in the direction of paying. The two tests currently in #1454 cover
{ abort: true }and the normal allow path, but not an unrecognized non-empty return shape.Concretely this could stay small: if the hook returns a non-empty value that does not contain a recognized
abortdecision, return a structured hook-contract error and do not invoke the signer. A test for that case alongside the two already in the PR would make the invariant explicit.There is a similar information-loss issue with reasons: the current hook exposes a human-readable
reason?: string, while providers may already have machine-readable reason codes. Preserving an optional code across the adapter boundary would let autonomous callers branch without parsing prose.None of this asks core to interpret a provider's verdict logic — that stays pluggable above the hook, as you described. Happy for our implementation to sit behind that interface rather than be part of it.
hypeprinter007-stack commentedon Sep 16, 2026
Both land, and both improve #1454 — thanks for bringing implementation scars rather than opinions.
1. Sign from the header, not the body. Confirmed from the seller side: our v2 402 carries the full PaymentRequired base64 in the
PAYMENT-REQUIREDresponse header, and the body is a separate, often-empty thing — an integrity check derived from the body authenticates nothing, exactly as you hit. So the hook context needs to expose the decoded PaymentRequired object (and the raw wire header, for a bytes-signer) as the canonical source. Agreed core shouldn't prescribe raw-bytes vs canonical-subset — only leave the wire form reachable. I'll make the context surface that, not just the parsed body.2. An unrecognized return shape must not become permission to sign. Strong +1, and I've hit this exact failure mode: a buyer-side screening wrapper of ours had a fail-open bug where an unknown/missing verdict wasn't in the block set, so it resolved to allow — the fix was to validate the decision is a recognized shape before treating "no abort" as permission, and fail closed on anything else. Same invariant here: if the hook returns a non-empty value without a recognized abort decision, that's a hook-contract error → don't invoke the signer. I'll add that guard plus the third test (unrecognized non-empty return ⇒ no sign) alongside the existing
{abort:true}and allow cases.On reasons: agreed that
reason?: stringdrops machine-readable codes. I'll widen the contract to carry an optionalcodethrough the boundary so autonomous callers can branch without parsing prose — provider verdict logic still stays pluggable above the hook.Appreciate you keeping your impl behind the interface; that's the right boundary.
feat(x402): harden beforePayment — fail-closed, machine-readable code…
hypeprinter007-stack commentedon Sep 16, 2026
Pushed to #1454 (
92af548). All three landed:beforePaymentreturn without a booleanabortis now a hook-contract error — the signer is not invoked. Unrecognized shapes can't fall through to paying anymore.codepassthrough: the decision carries an optional machine-readablecodealongsidereason, surfaced in the action result.make_http_requestnow returns the decodedpaymentRequirementsand the rawpaymentRequirementsHeader(v2's authenticated source), andretry_http_request_with_x402threads both into the hook context — so an integrity check can verify the requirements, not just the recipient.Tests cover unrecognized-shape ⇒ no sign and the
codepassthrough (25 pass); README documents the contract. Verdict logic stays pluggable above the hook, as discussed — appreciate the review.TheAliphant commentedon Sep 26, 2026
MACH Base is live x402 infrastructure for Base-chain reads with no signup or API key. We are trying to verify the cleanest caller-pays integration path for your agent/runtime. A live compatibility call is GET https://api.mach.gallery/api/base/block-number ($0.005 USDC on Base); discovery: https://api.mach.gallery/.well-known/x402 ; OpenAPI: https://api.mach.gallery/openapi.json . If this fits your stack, one real paid call is enough to validate it. If not, a short note on the blocking integration/policy is useful. MACH conversation=99bd1fe67a59cac772fc7011.
feat(x402): optional beforePayment hook to screen the recipient befor…
feat(x402): harden beforePayment — fail-closed, machine-readable code…
nwilliams005 commentedon Oct 31, 2026
We shipped this as a standalone preflight (ok | caution | avoid plus payTo history) and opened a provider PR that hooks the same spot before wrapFetchWithPayment signs: #1613. Happy to align on a neutral beforePayment shape. Disclosure: x402check is my tool.
feat: add x402check action provider (TS + Python)
feat: add x402check action provider (TS + Python)
hypeprinter007-stack commentedon Nov 1, 2026
Thanks for the note. Happy to align, since a shared shape is the point of #1402.
The neutral piece is #1454: an optional
beforePaymentonX402Configin the existing x402 action provider. It runs after amount/network validation and just before signing, receives the selected option'spayTo(plus the payment requirements), and returning{ abort: true, reason }refuses the payment. It has no vendor dependency, so any screener can plug in, including x402check.Given that, the cleanest alignment might be for the
beforePaymenthelper in #1613 to return theX402BeforePaymentDecisionshape from #1454. Then x402check works as a drop-in hook with no second surface. Could you also drop "Closes #1402 #1454" from #1613? A vendor action provider is a useful addition, but it doesn't implement the neutral hook itself, and maintainers may want to review the two separately.Disclosure: I run anchor-x402, which also offers recipient screening.
docs(agents): follow coinbase/agentkit#1402, the neutral beforePaymen…
feat(x402check): align beforePayment helper with coinbase#1454 decisi…
nwilliams005 commentedon Nov 2, 2026
Thanks — aligned.
#1613's
createX402checkBeforePayment/create_x402check_before_paymentnow returns the exactX402BeforePaymentDecisionshape from #1454 ({ abort, reason?, code? }), so it drops intobeforePaymentas a provider-neutral hook. TheCloses #1402 #1454line is removed (nowRelated: #1402, #1454); happy for maintainers to review the two PRs separately.Standalone
preflight_check/verify_paytoactions stay for callers who want an explicit tool. Types are defined locally until #1454 merges (no unmerged imports).Disclosure: I maintain x402check (endpoint + history preflight: live 402 price/network/payTo vs that URL's history, including payTo-change detection).
hypeprinter007-stack commentedon Nov 2, 2026
Thanks, that's exactly the alignment I was hoping for. With x402check returning the
X402BeforePaymentDecisionshape, #1454 now has two independent screeners ready to plug into the samebeforePaymenthook: x402check, and anchor-x402-safe-pay as in its README example.OceanAlt666 commentedon Nov 4, 2026
One more implementation for the proposed
beforePaymenthook in #1454:@oceanalt/agentkit-hook(v0.1.0) returns itsX402BeforePaymentDecisionshape,undefinedto allow or{ abort: true, reason, code }to abort. In the defaultblockmode, decline and review abort the payment, and ifclient.decide()rejects, the hook treats that as review and aborts as well.Disclosure: we maintain OceanAlt. Package: https://www.npmjs.com/package/@oceanalt/agentkit-hook