Registering Payout Addresses and Spend Policy
Register a payee payout address with a proof-of-ownership signature so charge() can resolve pay_to, and configure the org's spend policy to cap and allowlist outbound x402 payments.
Every payee registers a payout address exactly once before it can receive x402 payments. Every org can also configure a spend policy that caps and allowlists the payments it sends. Do the first before your first charge(); do the second any time you want a kill-switch, per-payment/daily caps, or a payee allowlist on outbound payments.
For the full non-custodial payment model (four-step flow, amount formats, networks), see x402 Payments Overview. For creating and settling challenges once a payout address exists, see Creating and Paying Challenges.
Register a payout address#
X402Client.register_payout_address is the Python entry point for x402 payout address registration: it proves control of a wallet with a local ownership signature and sets that address as your org's default payout destination for a network, so charge() can resolve pay_to.
- 1
Create the x402 client and signer#
import os import primitive x402 = primitive.create_x402_client(api_key=os.environ["PRIMITIVE_API_KEY"]) payee = primitive.PrivateKeySigner(os.environ["PAYEE_KEY"])PrivateKeySignerholds the wallet key in process memory. The key is never sent to Primitive; it signs the ownership message locally. - 2
Register the address#
result = x402.register_payout_address( signer=payee, network="base-sepolia", label="treasury", ) print(result.is_default, result.address, result.verified_at)networkisbase-sepolia(testnet) orbase(mainnet).labelis optional; it comes back on each entry fromlist_payout_addresses. - 3
Verify the address is registered#
addresses = x402.list_payout_addresses() print(addresses)Expect at least one entry with
network="base-sepolia"andis_default=True. This is the addresscharge()will fill in aspay_tofor challenges you create on that network.
Register once per network you plan to charge on. There's no per-charge payout address argument, charge() always resolves pay_to from your org's registered default for the challenge's network.
register_payout_address requires a signer that supports the ownership-proof signature (sign_message under the hood). Passing a signer that can only do EIP-712 typed-data signing (the kind pay() needs) raises an X402Error naming the missing capability.
Read and update the spend policy#
The spend policy guards every outbound x402 payment your org makes: a paused kill-switch, max_per_payment and max_per_day caps expressed in token base units (USDC has 6 decimals, so "5000000" is 5.00 USDC), and an allowlist of payee org ids. Read it with x402.get_spend_policy() and update it with x402.set_spend_policy(...).
| Field | Meaning |
|---|---|
paused | When True, all outbound payments are refused. |
max_per_payment | Cap in token base units, or None for no cap. |
max_per_day | Daily cap in token base units, or None for no cap. |
allowlist | Allowed payee org ids. None means any on-net payee is allowed; [] denies all. |
policy = x402.get_spend_policy()
print(policy.paused, policy.max_per_payment, policy.max_per_day, policy.allowlist)
set_spend_policy merges: only the fields you pass change, and omitted fields keep their current value.
x402.set_spend_policy({"paused": False, "max_per_payment": "5000000"})
Pass None explicitly for a field to clear that cap:
x402.set_spend_policy({"max_per_day": None})
Use paused: True as an emergency stop for outbound payments without touching caps or the allowlist, a single-field update leaves everything else as-is.
List registered payout addresses#
X402Client.list_payout_addresses returns every payout address registered for your org, across all networks:
for addr in x402.list_payout_addresses():
print(addr.network, addr.address, addr.label, addr.is_default)
Each entry carries id, address, network, label, is_default, and verified_at.
Errors#
Every method on this page raises primitive.X402Error on a client-side, transport, or non-2xx server error. register_payout_address, get_spend_policy, set_spend_policy, and list_payout_addresses all raise primitive.X402Error on a client-side, transport, or non-2xx server error. status is the HTTP status, or 0 when the request never reached the server; body carries the parsed error envelope when present; retry_after carries the Retry-After header when the server sent one. See the Python SDK Error Reference for the full catalog.
Next steps#
Create a payment challenge as the payee and settle it as the payer once your payout address is registered.
Email-Native PaymentsIssue and pay an x402 challenge that rides a real email thread instead of an out-of-band channel.
Low-Level Payment SigningDrive nonce derivation, validity windows, and EIP-712 signing directly when pay() doesn't fit your flow.
x402 Payments OverviewReview the shared non-custodial payment model across every SDK before customizing your integration.
Was this page helpful?