Documentation Index: Fetch llms.txt first to discover every published page. This page is also available as Markdown at /x402-payments-overview-04a296ff/go-x402-register-payout.md.
Verified · 8/11/2026

Registering a Payout Address

Prove control of a wallet with a local ownership signature and register it as your org's default x402 payout destination on a given network, a one-time step before you can call Charge.

Register a payout address once, per network, before you call Charge. The registration proves you control a wallet by signing an ownership message locally with your private key; the recovered address becomes the pay_to destination Charge resolves automatically for every future challenge on that network.

Do this the first time you set up payments as a payee, and again whenever you want to change which wallet receives funds on a given network. You don't need it to pay a challenge, only to receive.

Note

x402 payments are non-custodial: your private key never leaves your machine, and Primitive never holds funds. For the full payment model (charge → pay → settle) and terminology, see x402 Payments Overview.

What the registration proves#

Registration proves you control the wallet: it sends a locally signed ownership message, never your private key.

RegisterPayoutAddress posts that signature alongside the address. The message is built by BuildPayoutRegistrationMessage(org, address, network, issuedAt) and must be byte-identical to what the platform recomputes:

Primitive x402 payout address authorization

I authorize this address as a payout destination for my Primitive organization.

org: <org-id>
address: <lowercased address>
network: <network>
issued: <issued-at>

Your organization id is embedded in the signed bytes. That means a captured signature can never be replayed to register the address under a different org. The org id is resolved automatically from your API key, so you never set it yourself; the X402ClientOptions.APIKey you construct the client with determines it.

Prerequisites#

You need a Primitive API key, a wallet private key in an environment variable, and the Go SDK installed.

  • A Primitive API key (prim_test in examples, or your production key via PRIMITIVE_API_KEY).
  • A wallet private key for the address you want paid, in an environment variable such as PAYEE_KEY. Never hardcode it.
  • The Go SDK installed: go get github.com/primitivedotdev/sdks/sdk-go@latest.
  1. 1

    Construct the x402 client#

    Build a client from your API key. With zero options it reads PRIMITIVE_API_KEY from the environment and targets the production host.

    package main
    
    import (
    	"context"
    	"log"
    	"os"
    
    	primitive "github.com/primitivedotdev/sdks/sdk-go"
    )
    
    func main() {
    	ctx := context.Background()
    
    	client := primitive.NewX402Client(primitive.X402ClientOptions{
    		APIKey: os.Getenv("PRIMITIVE_API_KEY"),
    	})
    	_ = ctx
    	_ = client
    }
    
  2. 2

    Build a signer from your private key#

    NewPrivateKeySigner holds the wallet key in process memory and never sends it to Primitive. It signs both the EIP-712 payment authorization (used by Pay) and the personal-sign ownership message (used here).

    payee, err := primitive.NewPrivateKeySigner(os.Getenv("PAYEE_KEY"))
    if err != nil {
    	log.Fatal(err)
    }
    
  3. 3

    Register the address#

    Call RegisterPayoutAddress with the target network and, optionally, a human-readable label. Leave Org unset unless you need to override the organization resolved from your API key.

    label := "treasury"
    result, err := client.RegisterPayoutAddress(ctx, primitive.X402PayoutRegistrationInput{
    	Network: "base-sepolia",
    	Label:   &label,
    }, payee)
    if err != nil {
    	log.Fatal(err)
    }
    
  4. 4

    Verify it landed#

    List your registered payout addresses to confirm the new default:

    addresses, err := client.ListPayoutAddresses(ctx)
    if err != nil {
    	log.Fatal(err)
    }
    for _, a := range addresses {
    	log.Println(a)
    }
    

    A successful registration means later calls to Charge on that network resolve PayTo to this address automatically; you never pass a payout address on Charge itself.

Networks#

Two networks are supported: "base-sepolia" (testnet, used in these examples) and "base" (mainnet). Register separately per network; a registration on one network does not carry over to the other.

Warning

Registering a new default address for a network replaces the previous one for future challenges. Any challenge already created before the change keeps its original pay_to.

Tip

You only register a payout address as the payee. If you're the payer signing and settling someone else's challenge, skip this step entirely; see x402 Payments Overview for the payer side of the flow.

Errors#

Every method on X402Client, including RegisterPayoutAddress, returns a *primitive.X402Error on a client-side, transport, or non-2xx server error. Use errors.As to inspect it:

import (
	"errors"
	"log"

	primitive "github.com/primitivedotdev/sdks/sdk-go"
)

var xerr *primitive.X402Error
if errors.As(err, &xerr) {
	log.Printf("status=%d retryAfter=%v body=%v", xerr.Status, xerr.RetryAfter, xerr.Body)
}

Status is 0 when the request never reached the server (a DNS failure, a connection error, or a client-side timeout), distinct from a 4xx/5xx the server actually returned. See x402 Errors for the full breakdown of status codes and retry semantics.

Next steps#

Was this page helpful?

© Primitive SDKs

Powered by Browzer