402 response (committing to payment terms) and a receipt on every 200 response (confirming service delivery). No changes to your business logic.
Why Enable Offer & Receipt Signing?
Signed offers and receipts are portable, verifiable artifacts that any third party can check. They enable:- Reputation systems — Clients can attach receipts to onchain attestations as proof they actually paid for and received a service. This is the “Verified Purchase” equivalent for the open web.
- Dispute resolution — Offers prove the server committed to specific terms; receipts prove delivery. If either party disputes a transaction, the signed artifacts provide evidence.
- Auditing — Receipts create a verifiable trail of service delivery without exposing transaction details (the transaction hash is optional).
- Client confidence — Services with verifiable proof-of-interaction build stronger trust signals, making new clients more likely to use the service.
Prerequisites
- An existing x402 resource server (or a new Express.js project)
- Node.js 18+
- A facilitator URL (see Quickstart for Sellers)
Installation
Signing Formats
The extension supports two signature formats. Choose based on your key management setup:
Both formats produce equivalent proof artifacts. Clients and verifiers handle both transparently.
Quick Start: EIP-712 with Environment Variables
This example uses EIP-712 signing with a raw private key from an environment variable. This is the simplest way to get started.Key Security: Storing private keys in environment variables works for production as long as you keep them secret and rotate them regularly. For higher security guarantees, use a key management service (KMS), hardware security module (HSM), or a managed wallet provider. See Production Key Management below for options. When rotating keys, use a signer-authorization service (see Ecosystem Trust Providers) to maintain verifiable continuity of authorization.
Signing Key ≠ Payment Address: The signing key used for offers and receipts should be a dedicated signing key, not the wallet that receives payments (payTo). Separating signing from payment receipt limits exposure if the signing key is compromised.
Environment Variables
Create a.env file:
Server Setup (EIP-712)
What Happens Automatically
Once configured, the extension hooks into the x402 payment flow:-
On
402responses: The extension signs an offer for each entry inaccepts[]and includes them in the response’sextensionsfield. Each offer contains the payment terms (scheme,network,amount,payTo) and avalidUntiltimestamp. -
On
200responses (after successful payment): The extension signs a receipt containing theresourceUrl,payeraddress,network, andissuedAttimestamp. The receipt is included in thePAYMENT-RESPONSEheader’sextensionsfield.
Alternative: JWS Signing with did:web
JWS signing uses a did:web identifier, which means your server must host a DID document at /.well-known/did.json. Clients and verifiers resolve this document to find your public key so they can verify the signature.
JWS supports a wider range of key types than EIP-712 (secp256k1 only), including secp256r1 (EC P-256), Ed25519, and secp256k1 (ES256K). If your infrastructure is enterprise-oriented or Solana-native (Ed25519), JWS lets you use your existing key infrastructure.
Environment Variables
Server Setup (JWS)
Hosting the DID Document
For JWS verification, clients resolve yourdid:web to find the public key. Serve the DID document at /.well-known/did.json:
Configuration
ThedeclareOfferReceiptExtension function accepts an optional configuration object:
What Gets Signed
Offer Payload
Each offer is signed when the server returns a402 Payment Required response:
Receipt Payload
Each receipt is signed when the server returns a200 response after successful payment:
Both payloads are signed using the format configured on the server (EIP-712 or JWS). The signed artifacts are self-contained — a verifier only needs the artifact and the signer’s public key to verify.
Production Key Management
Environment variables are not ideal for storing signing keys, but if you use them keep them secret and rotate them periodically. The main risks are leaking through process inspection, logging, crash dumps, or container metadata endpoints — standard operational security applies. For higher security guarantees, use a signing backend that keeps keys in secure hardware or managed infrastructure. The extension’s signer interface is pluggable — you only need to implement thesign() function (for JWS) or signTypedData() function (for EIP-712) using your provider’s SDK. The OfferReceiptIssuer interface handles the rest.
Key rotation: When you rotate signing keys (whether stored in environment variables or KMS), previously-issued offers and receipts remain valid as long as verifiers can confirm the old key was authorized at issuance time. Use a signer-authorization service (see Ecosystem Trust Providers) to record key-binding attestations that survive rotation.
When using a managed wallet provider, you won’t have access to the raw private key. Instead, you call the provider’s signing API. Here’s what the EIP-712 setup looks like with a server wallet (conceptual example):
privateKeyToAccount — instead, you pass a function that delegates signing to the provider’s API. Any managed wallet provider that supports signTypedData (for EIP-712) or raw signing (for JWS) works as a drop-in replacement.
Binding Your Signing Key to Your Service Identity
Signing offers and receipts is only half the story. For verifiers to trust that your signatures are legitimate, they need to confirm that your signing key is authorized to act on behalf of your service’s identity (did:web:yourdomain.com).
DID Document (did.json)
If you’re using JWS signing, you’re already hosting a DID document at /.well-known/did.json (see JWS setup above). This document declares which keys are authorized for your did:web identity. Verifiers resolve your DID and check that the signing key is listed in verificationMethod.
If you’re using EIP-712 signing, you can host a did.json as well — list your EIP-712 signing address as a verificationMethod so verifiers can confirm the key is authorized for your domain.
This is a W3C standard mechanism and is sufficient for many use cases. However, the DID document is mutable — if you remove the key later, verifiers checking at that point won’t find it.
Signer Authorization Approaches
The following approaches can establish that a signing key is authorized for your service. They differ in durability — specifically whether authorization evidence survives key rotation and whether it provides temporally immutable proof.Adding to this table: Solutions that provide signer-authorization evidence for x402 signed artifacts are welcome. To be listed, describe how the approach handles key rotation and whether it provides temporally immutable proof of authorization. Open a PR or issue against this repository.These mechanisms are not optional — verifiers need at least one way to confirm your signing key is authorized for your service. The choice depends on your durability requirements.
Client-Side: Extracting Offers and Receipts
The@x402/extensions package provides client utilities for extracting and verifying the signed artifacts your server produces.
Extract Offers from a 402 Response
Extract a Receipt from a 200 Response
verifyReceiptMatchesOffer checks that:
resourceUrlmatches the offernetworkmatches the offerpayermatches one of your wallet addressesissuedAtis recent (within 1 hour by default)
What Can Clients Do with These Artifacts?
Signed offers and receipts are portable, verifiable artifacts. Clients can:- Attach them to reputation attestations as proof-of-interaction (e.g., “Verified Purchase” reviews)
- Store them for auditing — receipts create a verifiable trail of service delivery
- Use them in dispute resolution — offers prove the server committed to terms; receipts prove delivery
- Share them with aggregators — trust scoring engines can verify the signatures independently
- Submit them to trust providers — services like OMATrust can embed receipts into user reviews, and PEAC Protocol can record them as payment evidence attestation chains
Working Examples
Complete working examples are available in the x402 repository:- Server Example (Express.js) — Resource server with offer-receipt enabled, showing both EIP-712 and JWS configurations
- Client Example — Complete client flow: offer extraction, payment, receipt capture, and verification
Further Reading
- Extensions Overview — How the x402 extension system works
- Offer & Receipt Extension Specification — Full protocol spec with payload schemas, EIP-712 types, verification rules, and wire format examples
- @x402/extensions package — TypeScript implementation source
- SDK Features — Extension support across TypeScript, Go, and Python