---
title: "Spend Policy and Payout Address Management"
canonical: "https://test.abhinandan.one/x402-payments-overview-04a296ff/node-sdk-x402-spend-policy"
markdown_url: "https://test.abhinandan.one/x402-payments-overview-04a296ff/node-sdk-x402-spend-policy.md"
publisher: "Primitive SDKs"
kind: "guide"
content_type: "reference"
category: "Node.js SDK"
parent: "x402-payments-overview-04a296ff"
description: "setSpendPolicy merges a paused kill-switch, per-payment and daily USDC caps, and a payee allowlist onto your org's outbound x402 payments."
keywords: ["setSpendPolicy", "getSpendPolicy", "listPayoutAddresses", "x402 spend policy", "max_per_payment", "max_per_day"]
last_modified: "2026-08-21T18:22:43.359885+00:00"
published_at: "2026-08-11T18:54:58.058487+00:00"
source_files:
  - "sdk-node/README.md"
  - "sdk-node/src/x402/client.ts"
sections:
  - {anchor: "what-the-spend-policy-controls", title: "What the spend policy controls"}
  - {anchor: "read-the-current-policy", title: "Read the current policy"}
  - {anchor: "update-the-policy", title: "Update the policy"}
  - {anchor: "step-decide-which-fields-to-change", title: "Decide which fields to change"}
  - {anchor: "step-call-setspendpolicy-with-only-those-fields", title: "Call setSpendPolicy with only those fields"}
  - {anchor: "step-pass-null-to-clear-a-cap", title: "Pass null to clear a cap"}
  - {anchor: "step-verify-with-a-fresh-read", title: "Verify with a fresh read"}
  - {anchor: "kill-switch-pause-all-outbound-payments", title: "Kill-switch: pause all outbound payments"}
  - {anchor: "allowlist-a-fixed-set-of-payee-orgs", title: "Allowlist a fixed set of payee orgs"}
  - {anchor: "list-registered-payout-addresses", title: "List registered payout addresses"}
  - {anchor: "errors", title: "Errors"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Spend Policy and Payout Address Management

Guard outbound x402 payments with an org-level kill-switch, per-payment and daily caps, and a payee allowlist, and inspect the payout addresses your org has registered.

Every outbound x402 payment your org makes with `pay()` is checked against a **spend policy**: a paused kill-switch, per-payment and daily caps in token base units, and an allowlist of payee orgs. Reach for this page when you need to cap exposure before handing out payment credentials to an agent, or when you need to see which wallet addresses your org has already registered as payout destinations.

For the payment flow itself (registering a payout address as payee, creating a challenge, paying it), see [Charging and Registering Payout Addresses](https://test.abhinandan.one/x402-payments-overview-04a296ff/node-sdk-x402-charging.md) and [Paying a Challenge](https://test.abhinandan.one/x402-payments-overview-04a296ff/node-sdk-x402-paying.md). This page covers the two read/update operations that sit alongside that flow: `getSpendPolicy` / `setSpendPolicy` and `listPayoutAddresses`.

## What the spend policy controls

The [spend policy](https://test.abhinandan.one/x402-payments-overview.md) is the org-level guardrail on outbound x402 payments, with four fields: a `paused` kill-switch, a `max_per_payment` cap, a `max_per_day` cap (both in token base units), and an `allowlist` of payee org ids.

> **Info:** The spend policy guards **outbound** payments only, payments your org makes with `pay()`. It has no effect on payments your org receives via `charge()`.

| Field | Type | Meaning |
| --- | --- | --- |
| `paused` | `boolean` | Kill-switch. When `true`, every outbound payment is refused. |
| `max_per_payment` | `string \| null` | Per-payment cap, in token base units. `null` means no cap. |
| `max_per_day` | `string \| null` | Daily cap, in token base units. `null` means no cap. |
| `allowlist` | `string[] \| null` | Allowed payee org ids. `null` means any on-net payee is allowed; `[]` denies every payee. |

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

## Read the current policy

Call `getSpendPolicy()` on the x402 client to read all four fields as the server currently holds them.

```typescript
import { createX402Client } from "@primitivedotdev/sdk/x402";

const x402 = createX402Client({ apiKey: process.env.PRIMITIVE_API_KEY! });

const policy = await x402.getSpendPolicy();
console.log(policy);
// {
//   paused: false,
//   max_per_payment: "5000000",
//   max_per_day: null,
//   allowlist: null,
// }
```

## Update the policy

Call `setSpendPolicy()` with only the fields you want to change; omitted fields keep their current server-side value.

### 1. Decide which fields to change

`setSpendPolicy` **merges**: only the fields you pass in the update change, and any field you omit keeps its current server-side value. There is no full-replace mode, so you never need to re-send the whole policy just to change one field.

### 2. Call setSpendPolicy with only those fields

```typescript
// Cap every single payment at 5.00 USDC (5,000,000 base units).
// paused and allowlist are left untouched.
await x402.setSpendPolicy({ paused: false, max_per_payment: "5000000" });
```

### 3. Pass null to clear a cap

`null` is a real value here, distinct from omitting the field. Passing `null` explicitly removes a previously-set cap:

```typescript
// Remove the daily cap; max_per_payment (and paused, allowlist) are unchanged.
await x402.setSpendPolicy({ max_per_day: null });
```

### 4. Verify with a fresh read

```typescript
const updated = await x402.getSpendPolicy();
console.log(updated.max_per_payment); // "5000000"
```

### Kill-switch: pause all outbound payments

Setting `paused: true` refuses every outbound payment regardless of caps or allowlist. Use it as an emergency stop when you suspect an agent is misbehaving:

```typescript
await x402.setSpendPolicy({ paused: true });
```

### Allowlist a fixed set of payee orgs

`allowlist` takes payee org ids. `null` (the default) allows any on-net payee; an empty array denies every payee, which is useful for a policy you want fully closed until you explicitly open it:

```typescript
// Only allow payments to two specific payee orgs.
await x402.setSpendPolicy({
  allowlist: ["org_9f2a1b", "org_44de70"],
});

// Deny every payee (fully closed).
await x402.setSpendPolicy({ allowlist: [] });

// Allow any on-net payee again.
await x402.setSpendPolicy({ allowlist: null });
```

> **Warning:** Setting `allowlist: []` denies every payee, so every subsequent `pay()` call your org makes is refused. Confirm this is the intended blast radius before applying it in production.</br>

> **Tip:** Combine `max_per_payment` with `max_per_day` for defense in depth: `max_per_payment` bounds the damage from a single runaway call, `max_per_day` bounds it across a burst of calls.

## List registered payout addresses

`listPayoutAddresses()` returns every payout address your org has registered as a payee, each with its network, label, default flag, and verification timestamp. The one-time x402 payout address registration step is covered in [Charging and Registering Payout Addresses](https://test.abhinandan.one/x402-payments-overview-04a296ff/node-sdk-x402-charging.md).

```typescript
const addresses = await x402.listPayoutAddresses();
console.log(addresses);
// [
//   {
//     id: "...",
//     address: "0x1111111111111111111111111111111111111111",
//     network: "base-sepolia",
//     label: "treasury",
//     is_default: true,
//     verified_at: "2026-01-01T00:00:00.000Z",
//   },
// ]
```

Registration is per network, so an org can hold a default payout address on `base-sepolia` and another on `base`. `charge()` resolves its `pay_to` from this directory, which is why you register before requesting payments.

## Errors

Every method on the x402 client, including `getSpendPolicy`, `setSpendPolicy`, and `listPayoutAddresses`, throws `X402Error` on a client-side, transport, or non-2xx server error. `X402Error` carries `status` (`0` for a request that never reached the server), `body`, and `retryAfter`. See [Node.js SDK Errors](https://test.abhinandan.one/node-sdk-errors.md) for the full `X402Error` shape (`status`, `body`, `retryAfter`) and how to distinguish a request that never reached the server from a server-side rejection.
