> For the complete documentation index, see [llms.txt](https://docs.robotmoney.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.robotmoney.com/platform/give-an-agent-a-spending-limit.md).

# Give an Agent a Spending Limit

Define and sign a structured policy that RobotMoney evaluates for every action.

This page is optional for headless integrations.

Goal: author a structured policy, collect the required signatures, then evaluate actions against the active policy.

For the headless launch path, start with the [Headless Limits API](/govern/headless-limits-api.md). Those owner-managed windows cap decision-only authorizations for tenant agents that use `execution: "external"`. A signed policy remains the deeper protocol control for the full PaymentIntent execution path.

### Compose a Policy

Policy authoring uses the structured JSON DSL. RobotMoney does not currently compile plain English into a policy.

```typescript
const policy = await brain.policy.compose("tnt_acme", {
  version: 1,
  lists: {
    trusted_vendors: ["cp_acme_legal"],
  },
  rules: [
    {
      id: "small-trusted-payments",
      applies_to: ["outbound_payment"],
      when: {
        counterparty_in: "trusted_vendors",
        amount_lte: { currency: "USD", value: 5000 },
      },
      execute: "auto",
    },
    {
      id: "other-payments-require-review",
      applies_to: ["outbound_payment"],
      when: {},
      require: ["signer"],
      execute: "confirm",
    },
  ],
});

console.log(policy.policyId, policy.state, policy.signingPayload);
```

### Submit Signatures

Sign the returned payload with the authorized tenant member keys, then submit the resulting signatures.

```typescript
const result = await brain.policy.sign("tnt_acme", {
  policyId: policy.policyId,
  signatures: [
    { address: signerAddress, signature: signerSignature },
  ],
});

console.log(result.activated, result.policy.version);
```

`compose` returns `policyId`, `state`, and `signingPayload`. Policy activation happens as a side effect of `sign` once the required signatures are present.

### Test an Action

```typescript
const decision = await brain.policy.evaluate("tnt_acme", {
  kind: "outbound_payment",
  amount: { currency: "USD", value: "7800" },
  counterparty_id: "cp_acme_legal",
  agent_role: "payment",
});

console.log(decision.outcome);
console.log(decision.matched_rule_id);
console.log(decision.required_approvers);
```

`outcome` is exactly `allow`, `confirm`, or `reject`. It is not an SDK alias layer. This request records a `policy.evaluate` audit event, but it does not create a payment intent.

### Approval Requirements

Policy `require` contains the roles whose approvals the rule needs.

```typescript
const dualApprovalRule = {
  id: "large-payment",
  applies_to: ["outbound_payment"],
  when: {
    amount_gt: { currency: "USD", value: 50000 },
  },
  require: ["cfo", "ceo"],
  execute: "confirm",
};
```

### Update a Policy

Policies are versioned. Compose a new document with the next version, then submit signatures for that new `policyId`.

```typescript
const next = await brain.policy.compose("tnt_acme", {
  version: 2,
  lists: {},
  rules: [dualApprovalRule],
});

await brain.policy.sign("tnt_acme", {
  policyId: next.policyId,
  signatures: [{ address: signerAddress, signature: signerSignature }],
});
```

### What's Next

* [Pay an Invoice Safely](/platform/pay-an-invoice-safely.md)
* [Audit Every Action](/platform/audit-every-action.md)
