{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/x402-payments-overview-04a296ff/go-x402-register-payout","markdown_url":"https://test.abhinandan.one/x402-payments-overview-04a296ff/go-x402-register-payout.md","article":{"id":"ad0fbdbc-6019-48c9-ab2b-28440ecd2ac0","article_slug":"go-x402-register-payout","parent_article_slug":"x402-payments-overview-04a296ff","parent_article_title":"x402 Payments Overview","kind":"guide","published_at":"2026-08-11T18:54:57.685639+00:00","keywords":["RegisterPayoutAddress","x402 payout address registration","BuildPayoutRegistrationMessage","NewPrivateKeySigner","X402Client Go","pay_to resolution"],"meta_description":"Register a default x402 payout address in Go with RegisterPayoutAddress by signing a local ownership message with your wallet's private key.","og_image_url":null,"source_file_paths":["sdk-go/x402.go","sdk-go/README.md"],"recording_id":null,"replayable":false,"task_name":"Registering a Payout Address","category":"Go SDK","summary":null,"description":"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.","content_kind":"repo_page","content_markdown":"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.\n\nDo 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.\n\n<Note>\n\nx402 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](x402-payments-overview).\n\n</Note>\n\n## What the registration proves\n\nRegistration proves you control the wallet: it sends a locally signed ownership message, never your private key.\n\n`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:\n\n```text\nPrimitive x402 payout address authorization\n\nI authorize this address as a payout destination for my Primitive organization.\n\norg: <org-id>\naddress: <lowercased address>\nnetwork: <network>\nissued: <issued-at>\n```\n\nYour 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.\n\n## Prerequisites\n\nYou need a Primitive API key, a wallet private key in an environment variable, and the Go SDK installed.\n\n- A Primitive API key (`prim_test` in examples, or your production key via `PRIMITIVE_API_KEY`).\n- A wallet private key for the address you want paid, in an environment variable such as `PAYEE_KEY`. Never hardcode it.\n- The Go SDK installed: `go get github.com/primitivedotdev/sdks/sdk-go@latest`.\n\n<Steps>\n\n<Step title=\"Construct the x402 client\">\n\nBuild a client from your API key. With zero options it reads `PRIMITIVE_API_KEY` from the environment and targets the production host.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient := primitive.NewX402Client(primitive.X402ClientOptions{\n\t\tAPIKey: os.Getenv(\"PRIMITIVE_API_KEY\"),\n\t})\n\t_ = ctx\n\t_ = client\n}\n```\n\n</Step>\n\n<Step title=\"Build a signer from your private key\">\n\n`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).\n\n```go\npayee, err := primitive.NewPrivateKeySigner(os.Getenv(\"PAYEE_KEY\"))\nif err != nil {\n\tlog.Fatal(err)\n}\n```\n\n</Step>\n\n<Step title=\"Register the address\">\n\nCall `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.\n\n```go\nlabel := \"treasury\"\nresult, err := client.RegisterPayoutAddress(ctx, primitive.X402PayoutRegistrationInput{\n\tNetwork: \"base-sepolia\",\n\tLabel:   &label,\n}, payee)\nif err != nil {\n\tlog.Fatal(err)\n}\n```\n\n</Step>\n\n<Step title=\"Verify it landed\">\n\nList your registered payout addresses to confirm the new default:\n\n```go\naddresses, err := client.ListPayoutAddresses(ctx)\nif err != nil {\n\tlog.Fatal(err)\n}\nfor _, a := range addresses {\n\tlog.Println(a)\n}\n```\n\nA 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.\n\n</Step>\n\n</Steps>\n\n## Networks\n\nTwo 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.\n\n<Warning>\n\nRegistering 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`.\n\n</Warning>\n\n<Tip>\n\nYou 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](x402-payments-overview) for the payer side of the flow.\n\n</Tip>\n\n## Errors\n\nEvery 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:\n\n```go\nimport (\n\t\"errors\"\n\t\"log\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n)\n\nvar xerr *primitive.X402Error\nif errors.As(err, &xerr) {\n\tlog.Printf(\"status=%d retryAfter=%v body=%v\", xerr.Status, xerr.RetryAfter, xerr.Body)\n}\n```\n\n`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](go-x402-errors) for the full breakdown of status codes and retry semantics.\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Creating a Payment Challenge\" href=\"go-x402-create-charge\">\n\nUse your registered address as the payee and request a USDC payment with Client.Charge.\n\n</Card>\n\n<Card title=\"x402 Signing Primitives\" href=\"go-x402-signing-primitives\">\n\nDrive nonce derivation and EIP-712 signing yourself if Pay/RegisterPayoutAddress doesn't fit your signing flow.\n\n</Card>\n\n<Card title=\"x402 Spend Policy\" href=\"go-x402-spend-policy\">\n\nGuard outbound payments with caps, an allowlist, and a kill-switch once payments are flowing.\n\n</Card>\n\n<Card title=\"x402 Errors\" href=\"go-x402-errors\">\n\nInterpret X402Error status codes and indeterminate-outcome cases in detail.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-go/x402.go","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Registering+a+Payout+Address&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fgo-x402-register-payout","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}