Attribution enables onchain tracking of x402 payments by appending ERC-8021 Schema 2 builder codes to settlement transaction calldata. It records which application exposed the paid endpoint, which facilitator settled the payment, and which client initiated it.
Attribution is supported on EVM networks only, in the TypeScript and Go SDKs. The CDP Facilitator automatically appends its own wallet code (cdp_facil) to every settlement that includes attribution.
Get a builder code
Register at base.dev to generate your builder code. This code identifies your app in onchain transaction data for rewards and analytics.
Attribution codes
Three parties each contribute a code that is encoded into the onchain calldata suffix at settlement:
| Field | Name | Set by | Description |
|---|
a | App code | Resource server | Identifies the application that exposed the paid endpoint |
w | Wallet code | Facilitator | Identifies the facilitator that settled the payment — CDP uses cdp_facil |
s | Service code | Client (optional) | Identifies the client or intermediary that initiated the payment |
All codes must match the pattern ^[a-z0-9_]{1,32}$ — lowercase letters, digits, and underscores only, between 1 and 32 characters.
For resource servers
Declare your app code per-route in the payment middleware configuration. The code is included in the 402 Payment Required response so clients can echo it back at payment time.
npm install @x402/extensions
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { BUILDER_CODE, declareBuilderCodeExtension } from "@x402/extensions/builder-code";
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{
scheme: "exact",
price: "$0.001",
network: "eip155:8453",
payTo: "0xYourAddress",
},
],
description: "Weather data",
mimeType: "application/json",
extensions: {
[BUILDER_CODE]: declareBuilderCodeExtension("my_weather_app"),
},
},
},
new x402ResourceServer(facilitatorClient).register("eip155:8453", new ExactEvmScheme()),
),
);
go get github.com/x402-foundation/x402/go/v2
import (
"github.com/x402-foundation/x402/go/v2/extensions/buildercode"
x402http "github.com/x402-foundation/x402/go/v2/http"
)
builderCodeExt := buildercode.DeclareBuilderCodeExtension("my_weather_app")
extensions := make(map[string]interface{})
for k, v := range builderCodeExt {
extensions[k] = v
}
routes := x402http.RoutesConfig{
"GET /weather": {
Accepts: x402http.PaymentOptions{
{
Scheme: "exact",
Price: "$0.001",
Network: network,
PayTo: payTo,
},
},
Description: "Weather data",
MimeType: "application/json",
Extensions: extensions,
},
}
declareBuilderCodeExtension validates the format and throws if the code doesn’t match ^[a-z0-9_]{1,32}$.
For clients
Clients can optionally register a service code (s) to include their own attribution in every payment payload. The client SDK also automatically echoes the server’s app code (a) from the 402 response — this is required by the facilitator and happens automatically.
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { BuilderCodeClientExtension } from "@x402/extensions/builder-code";
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
client.registerExtension(new BuilderCodeClientExtension("my_client_app"));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("https://example.com/weather");
import (
x402 "github.com/x402-foundation/x402/go/v2"
exactevm "github.com/x402-foundation/x402/go/v2/mechanisms/evm/exact/client"
"github.com/x402-foundation/x402/go/v2/extensions/buildercode"
)
client := x402.Newx402Client()
client.Register("eip155:*", exactevm.NewExactEvmScheme(signer, nil))
client.RegisterExtension(buildercode.NewBuilderCodeClientExtension("my_client_app"))
CDP Facilitator behavior
When the CDP Facilitator settles a payment that includes attribution, it:
- Verifies that the client’s echoed
a matches the server’s declared a. A mismatch causes the settlement to be rejected.
- Reads
a (app code) and s (service code) from the payment payload.
- Adds its own wallet code:
w: "cdp_facil".
- CBOR-encodes all three fields and appends the ERC-8021 Schema 2 suffix to the settlement transaction calldata.
The resulting calldata suffix is structured as:
[CBOR-encoded map of a/s/w] [2-byte CBOR length] [0x02 schema ID] [16-byte ERC-8021 marker]
This suffix is readable by any offchain tool that knows the ERC-8021 format.
Verifying attribution on-chain
After a payment settles, you can read the attribution directly from the transaction calldata.
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";
import { parseBuilderCodeSuffixFromCalldata } from "@x402/extensions/builder-code";
const publicClient = createPublicClient({ chain: base, transport: http() });
const tx = await publicClient.getTransaction({ hash: txHash });
const attribution = parseBuilderCodeSuffixFromCalldata(tx.input);
if (attribution) {
console.log(attribution);
// { a: "my_weather_app", w: "cdp_facil", s: "my_client_app" }
}
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/x402-foundation/x402/go/v2/extensions/buildercode"
)
ethClient, _ := ethclient.Dial(rpcURL)
tx, _, _ := ethClient.TransactionByHash(ctx, common.HexToHash(txHash))
attribution, ok := buildercode.ParseBuilderCodeSuffixFromCalldata(common.Bytes2Hex(tx.Data()))
if ok {
fmt.Printf("%+v\n", attribution)
// {A: "my_weather_app", W: "cdp_facil", S: "my_client_app"}
}