{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/x402-payments-overview-04a296ff/go-x402-spend-policy","markdown_url":"https://test.abhinandan.one/x402-payments-overview-04a296ff/go-x402-spend-policy.md","article":{"id":"7c56e87e-adb9-4927-97a4-882a770a0541","article_slug":"go-x402-spend-policy","parent_article_slug":"x402-payments-overview-04a296ff","parent_article_title":"x402 Payments Overview","kind":"guide","published_at":"2026-08-11T18:54:59.562342+00:00","keywords":["SetSpendPolicy","X402SpendPolicyUpdate","ClearMaxPerPayment","ClearMaxPerDay","ListPayoutAddresses","spend policy Go SDK"],"meta_description":"Cap, pause, or allowlist outbound x402 payments in the Go SDK with X402Client.SetSpendPolicy and its builder methods.","og_image_url":null,"source_file_paths":["sdk-go/README.md","sdk-go/x402.go"],"recording_id":null,"replayable":false,"task_name":"x402 Spend Policy (Go SDK)","category":"Go SDK","summary":null,"description":"Guard outbound x402 payments with a paused kill-switch, per-payment and daily USDC caps, and a payee allowlist using the Go SDK's SetSpendPolicy.","content_kind":"repo_page","content_markdown":"## What the spend policy guards\n\nThe [spend policy](x402-payments-overview) is the org-level guardrail on outbound x402 payments: a `Paused` kill-switch, per-payment and daily caps in token base units (or `nil` for no cap), and an allowlist of payee orgs (`nil` means any on-net payee, an empty slice denies all). Set it before you let an agent pay challenges unattended, so a bug or a compromised key can't spend beyond what you've bounded.\n\nRead it with `client.GetSpendPolicy(ctx)` and change it with `client.SetSpendPolicy(ctx, update)`. Payout registration is covered in [Registering a Payout Address](go-x402-register-payout) and the charge/pay flow in [Creating a Payment Challenge](go-x402-create-charge); this page is only about the guardrail that sits in front of `Pay`.\n\n## Read the current policy\n\n`GetSpendPolicy` fetches the org's current policy over HTTP and returns the paused flag, both caps, and the allowlist.\n\n<Steps>\n\n<Step title=\"Construct the x402 client\">\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\tclient := primitive.NewX402Client(primitive.X402ClientOptions{\n\t\tAPIKey: os.Getenv(\"PRIMITIVE_API_KEY\"),\n\t})\n\n\tctx := context.Background()\n\n\tpolicy, err := client.GetSpendPolicy(ctx)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tlog.Println(policy)\n}\n```\n\n`NewX402Client` with zero options reads `PRIMITIVE_API_KEY` from the environment and targets the production host, same as any other x402 call.\n\n</Step>\n\n<Step title=\"Inspect the fields\">\n\nThe returned policy carries:\n\n| Field | Meaning |\n| --- | --- |\n| `Paused` | Kill-switch. `true` refuses every outbound payment. |\n| `MaxPerPayment` | Per-payment cap in token base units, or `nil` for no cap. |\n| `MaxPerDay` | Daily cap in token base units, or `nil` for no cap. |\n| `Allowlist` | Payee org ids allowed to receive payment. `nil` means any on-net payee; an empty slice denies all. |\n\nUSDC has 6 decimals, so a cap of `\"5000000\"` is 5.00 USDC.\n\n</Step>\n\n</Steps>\n\n## Update the policy\n\n`SetSpendPolicy` merges: only the fields you set on the `X402SpendPolicyUpdate` change, and every field you omit keeps its current server-side value. Build the update with its setter methods, which chain.\n\n<Steps>\n\n<Step title=\"Pause outbound payments (kill-switch)\">\n\n```go\nvar update primitive.X402SpendPolicyUpdate\nupdate.SetPaused(true)\n\npolicy, err := client.SetSpendPolicy(ctx, update)\nif err != nil {\n\tlog.Fatal(err)\n}\nlog.Println(\"paused:\", policy.Paused)\n```\n\nNothing else on the policy changes: caps and the allowlist stay exactly as they were before this call.\n\n</Step>\n\n<Step title=\"Set a per-payment cap\">\n\n```go\nvar update primitive.X402SpendPolicyUpdate\nupdate.SetPaused(false).SetMaxPerPayment(\"5000000\") // 5.00 USDC\n\npolicy, err := client.SetSpendPolicy(ctx, update)\nif err != nil {\n\tlog.Fatal(err)\n}\n```\n\nCaps are token base units as decimal strings, the same convention as `Charge`'s `Amount` field. Chain setters to change several fields in one call.\n\n</Step>\n\n<Step title=\"Clear a cap\">\n\n```go\nvar update primitive.X402SpendPolicyUpdate\nupdate.ClearMaxPerPayment()\nupdate.ClearMaxPerDay()\n\npolicy, err := client.SetSpendPolicy(ctx, update)\nif err != nil {\n\tlog.Fatal(err)\n}\n// policy.MaxPerPayment and policy.MaxPerDay are now nil (no cap).\n```\n\nUse `ClearMaxPerPayment` / `ClearMaxPerDay` to remove a cap. Because omitted fields keep their current value, there is no way to clear a cap by leaving it unset.\n\n</Step>\n\n</Steps>\n\n<Warning>\n\n`Paused: true` is the kill-switch: while it is set, every outbound payment is refused regardless of caps or allowlist. Flip it back with `update.SetPaused(false)` when you're ready to resume.\n\n</Warning>\n\n## List registered payout addresses\n\n`ListPayoutAddresses` returns every payout address registered for the org, including which one is the default per network.\n\nSpend policy governs outbound payment; the payout address directory is where inbound payment lands. List it alongside the policy when auditing an org's payment posture:\n\n```go\naddresses, err := client.ListPayoutAddresses(ctx)\nif err != nil {\n\tlog.Fatal(err)\n}\nfor _, addr := range addresses {\n\tlog.Println(addr)\n}\n```\n\nRegistering a new payout address is covered in [Registering a Payout Address](go-x402-register-payout).\n\n## Errors\n\nEvery spend-policy method returns a `*primitive.X402Error` on a client-side, transport, or non-2xx server error; use `errors.As` to inspect it. Check `Status` (`0` means the request never reached the server), `Body` (the parsed error envelope), and `RetryAfter`. See [x402 Errors](go-x402-errors) for the full error-handling reference.\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Registering a Payout Address\" href=\"go-x402-register-payout\">\n\nProve control of a wallet and register it as your default payout destination before you charge.\n\n</Card>\n\n<Card title=\"Creating a Payment Challenge\" href=\"go-x402-create-charge\">\n\nCreate an x402 challenge as the payee with Client.Charge.\n\n</Card>\n\n<Card title=\"x402 Errors\" href=\"go-x402-errors\">\n\nInterpret X402Error status codes, retry-after headers, and indeterminate outcomes.\n\n</Card>\n\n<Card title=\"x402 Payments Overview\" href=\"x402-payments-overview\">\n\nUnderstand the full non-custodial payment model shared across every SDK.\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/README.md","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+x402+Spend+Policy+%28Go+SDK%29&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fgo-x402-spend-policy","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}