---
title: "x402aff developer docs"
description: "Quickstart, configuration, payouts, and agent access for x402aff, the builder-code affiliation kit for x402 sellers on Base."
canonical: https://www.x402aff.xyz/developers
last-updated: 2026-09-30
---

# x402aff developer docs

> Quickstart, configuration, payouts, and agent access for x402aff, the builder-code affiliation kit for x402 sellers on Base.

## Quickstart

x402aff is a library you add to your own x402 server. There is no account, API key, or hosted service to sign up for. Get an app code at base.dev under Settings, Builder Codes, then install the SDK for your language.

Both SDKs ship the same Affiliation facade and resolve the identical split address, asserted byte for byte.

```bash
npm install x402aff viem
```

```ts
import { Affiliation } from "x402aff";

const aff = new Affiliation({ appCode: "bc_yourcode", sellerPayout: "0x..." });

// drop-in x402 DynamicPayTo for express, hono, next
app.use(paymentMiddleware({ payTo: aff.payTo, extensions: aff.extensions }));

// release payouts later (permissionless)
const { calls, balanceUnits } = await aff.release("bc_alice");
```

```bash
pip install x402aff
```

```python
from x402aff import Affiliation

aff = Affiliation(app_code="bc_yourcode", seller_payout=YOUR_WALLET)

PaymentOption(..., pay_to=aff.pay_to)        # per-request split
RouteConfig(..., extensions=aff.extensions)  # declares your code a

calls, balance = aff.release("bc_alice")     # permissionless payout
```

- [TypeScript guide](https://github.com/MiroShark/x402aff/blob/main/ts/README.md)
- [Integration guide (Python and protocol detail)](https://github.com/MiroShark/x402aff/blob/main/docs/INTEGRATION.md)

## Buyer side

TypeScript uses the official @x402/extensions/builder-code. Python ships BuilderCodeClientExtension. The buyer's app names its builder with an X-Builder-Code header and attaches the extension:

```ts
extensions: [builderCode("bc_alice")]
```

## How a payment flows

Base Builder Codes put three tags on a paid request: a (you, the API), s (the builder that drove it), w (the facilitator). The kit turns s into money with one move.

- Request (Buyer's app): The buyer's app calls your route with its builder code in an X-Builder-Code header.
- 402 (Your API): Your server answers with payTo set to the (you, builder) split address. Deterministic CREATE2, same every time.
- Settle (CDP facilitator): The stock CDP facilitator settles the buyer's gasless USDC payment straight into the split and writes a/s/w on-chain.
- Payout (0xSplits): Anyone calls distribute. 10% to the builder, 90% to you. Nobody can change where it goes.

## Configuration

- X402_BUILDER_SHARE_BPS: the builder's cut in basis points, default 1000 (10%), range 0 to 10000 (0 means attribution only). Or pass builderShareBps (TypeScript) / builder_share_bps (Python). The ratio is baked into the split address, so a new ratio opens a new split and old funds stay at the old ratio.
- X402_BASE_RPC: a paid Base RPC URL. The public RPC rate-limits, and a failed resolve falls back to your wallet, unsplit.
- More than two recipients (platform fee, partner, referrer): build a SplitPlan whose allocations sum to 10000; address prediction and distribute follow.

## Payouts and the claims dashboard

- aff.release(code) returns the calls that deploy (if needed) and distribute one builder's split. Anyone can send them.
- aff.pending() (or python3 -m x402aff.monitor) finds every builder who paid you straight from CDP's index, with no local ledger, and shows which splits are ready.
- aff.splits_payload() (Python) / aff.splitsPayload(cdpQuery) (TypeScript) returns every split, balance, deployed state, and a permissionless claim, ready for a GET /splits route.

```python
@app.get("/splits")
def splits(): return aff.splits_payload()
```

- [A live claims dashboard on Base mainnet](https://www.miroshark.xyz/x402aff)

## Find every kit payment

The buyer extension stamps a shared x402aff marker as a second s tag, so one query in the CDP SQL API finds every kit-routed payment across all sellers. It never changes a payout.

```sql
SELECT DISTINCT transaction_hash
FROM base.transaction_attributions
WHERE builder_code = 'x402aff' AND action = 1;
```

- [CDP SQL Playground](https://portal.cdp.coinbase.com/onchain-tools/sql-api)

## Any other language

Three view calls against two contracts. Port it to Go or Rust in an afternoon:

- Code to payout: payoutAddress(uint256) on the Base Builder Codes registry 0x000000BC7E6457e610fe52Dcc0ca5b3ce59C8E80 (token id is the code's ASCII bytes as a big-endian integer).
- Build the Split: recipients [builderPayout, seller], allocations [bps, 10000 - bps], totalAllocation 10000, incentive 0.
- Split to address: isDeployed(Split, 0x0, 0x0) on the 0xSplits PushSplit factory 0x8E8eB0cC6AE34A38B67D5Cf91ACa38f60bc3Ecf4 returns your payTo.

## Good to know

- Base mainnet with the CDP facilitator only: the registry and factory exist only there, and CDP writes the attribution.
- No keys, no custody: payTo is just an address in the 402, the buyer's payment is gasless, and distribute is permissionless.
- Distribute costs a few cents of gas. Payouts land about 2 base units light, because a split keeps 1 unit warm and floors each share.
- The s tag is a self-asserted routing tag, not signed proof of who drove a payment.
- Test against Base mainnet on a fork: the repo's fork-test suite runs 7 tests, 5 of them adversarial.

- [Fork tests](https://github.com/MiroShark/x402aff/tree/main/fork-test)

## For AI agents

Everything on this site is readable without JavaScript. Send Accept: text/markdown to any page, or append .md to its path, for a markdown copy. A read-only MCP server answers questions about the kit.

- MCP server (Streamable HTTP, no auth): https://www.x402aff.xyz/api/mcp
- REST API (GET, JSON, no auth): https://www.x402aff.xyz/api/v1/install, /api/v1/docs/{topic}, /api/v1/faq?q=, /api/v1/security. Contract: https://www.x402aff.xyz/openapi.json
- Agent skill: https://www.x402aff.xyz/skills/x402aff/SKILL.md
- Index for language models: https://www.x402aff.xyz/llms.txt

## Errors

The REST API answers errors as RFC 9457 problem details (application/problem+json) with a machine-readable code: unknown_endpoint (404), invalid_parameter or invalid_request (400), method_not_allowed (405), rate_limited (429), internal_error (500). The MCP server answers with JSON-RPC errors: -32700 parse error, -32600 invalid request, -32601 unknown method, -32602 unknown tool or bad arguments, -32603 internal error. Every REST response carries an API-Version header; breaking changes ship under a new path.

Rate limit: 60 requests per minute per client on the REST API. Every response reports the quota in RateLimit-Policy and RateLimit headers (plus RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset); a 429 adds Retry-After with the seconds to wait.

## Reference

- [GitHub repository](https://github.com/MiroShark/x402aff)
- [npm package](https://www.npmjs.com/package/x402aff)
- [PyPI package](https://pypi.org/project/x402aff/)
- [x402 protocol](https://x402.org)
- [Base Builder Codes](https://docs.base.org/apps/builder-codes/builder-codes)
- [0xSplits v2 audit](https://github.com/0xSplits/splits-contracts-monorepo/blob/main/audits/splits-v2.md)
