Add x402 payment completion telemetry event - #364
Merged
davedumto merged 1 commit intoAug 31, 2026
Merged
Conversation
Issue Vellar-Wallet#296: an x402 payment completing is not tracked as a telemetry event, so a host that wants to know what its agents are spending has to instrument every call site by hand. Adds contrib/examples/issue-296-x402-payment-telemetry: an x402.payment.completed event carrying the resource id and amount the issue asks for, plus the asset, network, settlement hash, payer, status and duration that make it reconcilable against chain. withPaymentTelemetry() wraps the public wallet.x402.fetch, so it works without SDK changes. Three properties drove the design. Telemetry must never break a payment: the event fires only after money has already moved, so a sink that throws, rejects, hangs, or whose onError itself throws still returns the caller's X402Response, and an async sink is not awaited so it adds no latency. It must not leak secrets: no headers or bodies are recorded, and resource URLs are stripped of query and fragment by default, since API keys live there. Amounts stay exact: base units are bigint, and toJSON renders them as decimal strings because JSON.stringify throws on a bigint and Number would silently round a stroop-precision value. Only settled payments emit. A 402-free response is a cache hit, not a payment, and counting it would inflate every metric built on this event. 27 tests cover the event shape, URL sanitization, bigint precision, and each way a sink can fail without disturbing the payment.
|
@Y33t-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@Y33t-dev is attempting to deploy a commit to the david's projects Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #296
Summary
x402 payment completion is not tracked as a telemetry event, so a host that wants to know what its agents are spending has to instrument every call site by hand.
This adds
contrib/examples/issue-296-x402-payment-telemetry: anx402.payment.completedevent carrying the resource id and amount the issue asks for, plus the asset, network, settlement hash, payer, status and duration that make it reconcilable against chain rather than merely countable.withPaymentTelemetry()wraps the publicwallet.x402.fetch, so it works today with no SDK change:Omit
sinkand the original function is returned untouched, so telemetry is opt-in with no per-call cost when off.Design rules
Telemetry must never break a payment. The event fires only after money has already moved on-chain. A sink that throws, rejects, hangs, or whose
onErroritself throws still returns the caller'sX402Response. An async sink is deliberately not awaited, so a slow analytics call adds no latency to a settled payment; its rejection is caught internally rather than surfacing as an unhandled rejection. There is no configuration that lets telemetry fail a settled payment.It must not leak secrets. No request headers or bodies are recorded.
PAYMENT-SIGNATUREis a signed authorization, and the SDK already treats leaking it as a security bug (packages/mcp-x402-payer/src/output.ts). Resource URLs are stripped of query and fragment by default, since API keys live in query strings;resourceIdModeallowsoriginor opt-infull. A URL that fails to parse still has its query cut off lexically.Amounts stay exact. Base units are
bigintand a stroop-precision value can exceedNumber.MAX_SAFE_INTEGER.JSON.stringifythrows on abigint, sotoJSON()rendersamountas a decimal string that round-trips throughBigInt(...).Only settled payments emit. A 402-free response is a cache hit, not a payment; counting it would inflate every metric built on this event. A failed payment emits nothing and rethrows untouched.
Tests
27 tests covering the event shape, URL sanitization, bigint precision and JSON round-tripping, and each way a sink can fail without disturbing the payment.
Notes for the maintainer
Two requirements could not be met as literally written, because contributor PRs may only touch
contrib/:README.mdis outsidecontrib/, so the reference documentation lives in the example's README instead, written so the tables can be lifted into that section verbatim.readSettlement()returns insrc/x402-client.ts, and an optionaltelemetry?: X402TelemetryOptionsonX402ClientDeps(threaded throughX402FacadeDeps.config, alongsidebudgetAttributes) would emit the same event for every caller. Happy to open that as a follow-up if you widen the scope.The 18 test failures on
dev(incontrib/examples/andsrc/session.test.ts, which also failstsc) are pre-existing and untouched by this branch.