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-spend-policy.md.
Verified · 8/11/2026

x402 Spend Policy (Go SDK)

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.

What the spend policy guards#

The spend policy 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.

Read it with client.GetSpendPolicy(ctx) and change it with client.SetSpendPolicy(ctx, update). Payout registration is covered in Registering a Payout Address and the charge/pay flow in Creating a Payment Challenge; this page is only about the guardrail that sits in front of Pay.

Read the current policy#

GetSpendPolicy fetches the org's current policy over HTTP and returns the paused flag, both caps, and the allowlist.

  1. 1

    Construct the x402 client#

    package main
    
    import (
    	"context"
    	"log"
    	"os"
    
    	primitive "github.com/primitivedotdev/sdks/sdk-go"
    )
    
    func main() {
    	client := primitive.NewX402Client(primitive.X402ClientOptions{
    		APIKey: os.Getenv("PRIMITIVE_API_KEY"),
    	})
    
    	ctx := context.Background()
    
    	policy, err := client.GetSpendPolicy(ctx)
    	if err != nil {
    		log.Fatal(err)
    	}
    	log.Println(policy)
    }
    

    NewX402Client with zero options reads PRIMITIVE_API_KEY from the environment and targets the production host, same as any other x402 call.

  2. 2

    Inspect the fields#

    The returned policy carries:

    FieldMeaning
    PausedKill-switch. true refuses every outbound payment.
    MaxPerPaymentPer-payment cap in token base units, or nil for no cap.
    MaxPerDayDaily cap in token base units, or nil for no cap.
    AllowlistPayee org ids allowed to receive payment. nil means any on-net payee; an empty slice denies all.

    USDC has 6 decimals, so a cap of "5000000" is 5.00 USDC.

Update the policy#

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.

  1. 1

    Pause outbound payments (kill-switch)#

    var update primitive.X402SpendPolicyUpdate
    update.SetPaused(true)
    
    policy, err := client.SetSpendPolicy(ctx, update)
    if err != nil {
    	log.Fatal(err)
    }
    log.Println("paused:", policy.Paused)
    

    Nothing else on the policy changes: caps and the allowlist stay exactly as they were before this call.

  2. 2

    Set a per-payment cap#

    var update primitive.X402SpendPolicyUpdate
    update.SetPaused(false).SetMaxPerPayment("5000000") // 5.00 USDC
    
    policy, err := client.SetSpendPolicy(ctx, update)
    if err != nil {
    	log.Fatal(err)
    }
    

    Caps are token base units as decimal strings, the same convention as Charge's Amount field. Chain setters to change several fields in one call.

  3. 3

    Clear a cap#

    var update primitive.X402SpendPolicyUpdate
    update.ClearMaxPerPayment()
    update.ClearMaxPerDay()
    
    policy, err := client.SetSpendPolicy(ctx, update)
    if err != nil {
    	log.Fatal(err)
    }
    // policy.MaxPerPayment and policy.MaxPerDay are now nil (no cap).
    

    Use 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.

Warning

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.

List registered payout addresses#

ListPayoutAddresses returns every payout address registered for the org, including which one is the default per network.

Spend 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:

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

Registering a new payout address is covered in Registering a Payout Address.

Errors#

Every 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 for the full error-handling reference.

Next steps#

Was this page helpful?

© Primitive SDKs

Powered by Browzer