Signed Is Not Settled: Logging Skavio x402 MCP Payments in Three States
An x402 payment can look deceptively simple in a client: a challenge appears, a wallet signs, and a tool returns a result. That sequence creates three separate records, not one “paid” flag. For a developer building an MCP agent or a paid-tool integration, the safer design is to label each state explicitly and keep authorization, chain observation and tool output out of the same ledger column. This guide uses Skavio x402 MCP as the concrete product context; supported product behavior belongs in the published documentation, while chain-level rules are for the integration to define and verify rather than assume.
Three events, three records
Treat the payment path as a state machine. The first event is a wallet signature over the payment challenge. It is authorization evidence produced by a key holder; it does not itself move tokens or place anything in a block. The second is a public-chain observation, where the integration checks the expected network, recipient and token for a matching transfer. The third is the tool result returned by the server. Tool discovery sits outside all three: a schema or listing response is not a paid invocation. Skavio's stated product rule for x402 MCP is that a signed payment challenge is not independently settled revenue. The same caution belongs in your UI copy and application logs.
- Challenge signed: authorization intent, not chain settlement.
- Observed on chain: a transfer fact under inspection, not yet a business decision.
- Tool result returned: service output, not accounting revenue.
- Discovery response: capability lookup, not payment.
What each state can and cannot prove
Use language that says what happened, not what you hope happened. A signature can be checked offline, but it is not proof that a token transfer exists. A transaction observation is evidence that a public-chain record matched the conditions you actually checked; how much confirmation the verifier requires is a rule the integration must state rather than assume. A returned tool result is evidence that the service responded; it does not turn the call into tax documentation, a bookkeeping entry or audited revenue. Skavio Payment Proof is a separate noncustodial USDC confirmation and evidence product, and even there payment verification does not itself constitute a tax invoice. Do not collapse the MCP payment path and the proof product into one accounting claim.
- To support an approval, cite the request identifier, challenge reference, expected network, recipient, token, amount and the verifier state you used.
- To support a ledger entry, require a business mapping and accounting review; chain evidence alone is not enough.
- To support a tool result, validate the output against the original request; a payment event does not make the result correct.
A log record that cannot be mistaken for revenue
Record the payment path as fields, not free text. Keep credentials out: no private keys, wallet signing material, API bearer keys or webhook secrets in application logs, browser code or support tickets. The fields below are a starting schema, not a statement that Skavio x402 MCP defines every one of them; align exact names with your own system and the published x402 documentation.
- request_id and idempotency_key: stable identifiers for the logical tool call and its retry path.
- challenge_id and challenge_signed_at: a reference to the signed intent; store status and identifiers, not secret material.
- expected_network, expected_token, expected_recipient, expected_amount: the conditions the integration actually checked.
- observed_tx_hash and observation_state: “not observed”, “observed” or “observed with mismatch”.
- verifier_confirmation_state: the verifier's own label; do not invent a universal “final” state.
- tool_result_state: “not returned”, “returned” or “returned but rejected after validation”.
- approval_actor and approval_time: the human or policy decision that allowed a paid retry or downstream action.
Put an approval gate before paid retries
Network timeouts and client restarts are normal. A retried paid call is exactly where a signed challenge can be mistaken for a completed purchase and where a second billable job can be created. Use one idempotency key per logical request, preserve the original challenge reference and reconcile payment state separately from HTTP status. A failed API call is not the same as a failed payment, and a successful challenge is not the same as a completed tool call. If the integration sees a payment challenge on an unexpected network or token, stop and escalate for human resolution instead of sending a replacement payment. Skavio's Processing API documentation offers an adjacent design vocabulary that is useful to borrow conceptually: the server quote at submission is authoritative, a successful job settles once and permanent failure releases the reservation. Do not copy that vocabulary into x402 records unless the x402 documentation defines the state you are logging.
- Reuse the same idempotency key on a retry; create a new one only for a genuinely new call.
- Log structured errors instead of free-text assertions that a transfer succeeded.
- Require explicit approval for autonomous agent spending and cap retry loops.
- Treat network, token or recipient mismatches as exceptions, not prompts to pay again.
Ship the labels before you ship the agent
UI copy should expose state. Use separate labels such as “challenge signed”, “observed on chain”, “confirmed by verifier” and “tool result returned”. If any label is missing, show “pending verification” or “not proven yet” rather than “paid”. This matters most for an agent that can call paid tools autonomously: one boolean invites downstream automation to treat authorization as revenue or fulfillment. Check the published Skavio x402 MCP documentation for supported discovery, challenge and tool behavior, and treat wallet support, fee treatment and confirmation rules as facts to verify before production rather than assumptions to hard-code.
- Show discovery and paid invocation as separate paths in the client.
- Preview the action and spending limit before an agent pays.
- Keep chain evidence reconciliation outside the MCP result handler.
- Re-read the documentation before production; supported behavior is the authority.