<!--
Sitemap:
- [Newton Agent NeoBank](/index)
- [Getting Started](/getting-started)
- [Onboard an Agent](/guides/onboard-an-agent)
- [Give an Agent a Budget](/guides/agent-budget)
- [Pay for an API](/guides/pay-for-an-api)
- [Issue a Card](/guides/issue-a-card)
- [Meter Your API](/guides/meter-your-api)
- [Handle Approvals](/guides/handle-approvals)
- [Listen for Events](/guides/listen-for-events)
- [SDK Essentials](/reference/sdk)
- [ModularVault](/reference/sdk/modular-vault)
- [PlatformClient](/reference/sdk/platform-client)
- [FacilitatorClient](/reference/sdk/facilitator-client)
- [createX402Fetch](/reference/sdk/create-x402-fetch)
- [buildPolicyParams](/reference/sdk/build-policy-params)
- [Networks & Addresses](/reference/networks)
-->

# Handle Approvals

Put a human in the loop for big payments. Set a per-token threshold, and any payment over it pauses for you to approve or reject — under-threshold payments still settle automatically.

## How it works

1. You set a `manual_approval_threshold` on an agent's token policy.
2. An agent pays over the threshold. The facilitator's verify step returns `pending_approval` with a `pendingIntentId` instead of settling — funds don't move yet.
3. You list the queued intents, then approve or reject each one.
4. Approving resumes settlement; rejecting frees the authorization so the agent can try again later.

Approving decides that one payment, not the agent — the agent stays authorized for everything under the threshold.

## Set a threshold

The threshold lives alongside the agent's daily limit and allowlist, per token. Add `manual_approval_threshold` (in the token's smallest units) to the agent's policy params and register it the usual way:

```typescript
import { PlatformClient } from "@newton-xyz/isaac";

const platform = new PlatformClient({
  baseUrl: process.env.PLATFORM_API_URL!,
  apiKey: process.env.OPERATOR_API_KEY!,
});

const params = {
  default_policy: "deny",
  agents: {
    "0xagentaddress": {
      tokens: {
        "0x8cfff8Bc9aA3d41fb9608496705CbfC83EBFc67c": {
          max_daily_spend: 500_000_000, // 500 USDC/day
          allowed_recipients: "*",
          manual_approval_threshold: 100_000_000, // queue anything over 100 USDC
        },
      },
    },
  },
};

const policy = await platform.getAgentPolicy(vaultAddress, "0xagentaddress");

await platform.updatePolicyParams(
  vaultAddress,
  "0xagentaddress",
  params,
  policy.policyId,
  policy.module,
  policy.expireAfter,
);
```

With no threshold set, nothing is ever queued — every in-budget payment settles straight through.

## See what's queued

When a payment is queued, the agent's payment attempt comes back as `pending_approval` rather than settled:

```typescript
const verify = await facilitator.verify(paymentPayload);

if (verify.policyStatus === "pending_approval") {
  console.log("queued for approval:", verify.pendingIntentId);
}
```

List everything waiting on a vault. The list is owner-or-approver scoped, and only shows intents that haven't expired:

```typescript
const { pending } = await platform.listPendingIntents(vaultAddress);

for (const intent of pending) {
  console.log(intent.id, intent.agent_address, intent.reason, intent.expires_at);
}
```

Each entry carries who queued it, why (`reason`), and when it expires. The dashboard shows the same inbox if you'd rather click.

## Approve or reject

Approving replays the payment and returns the settled result:

```typescript
const settled = await platform.approvePendingIntent(vaultAddress, intent.id);
console.log("settled in tx", settled.txHash);
```

Rejecting drops the payment and releases its reservation, so the same agent can re-attempt the same payment later:

```typescript
await platform.rejectPendingIntent(vaultAddress, intent.id);
```

A queued payment can't wait forever — each agent payment carries a signed spending window, and once it lapses the payment can no longer settle. Approving an expired one returns a conflict and flips the row to `expired` instead of settling. Approve or reject is also single-shot: a second decision on the same intent conflicts.

## Next

* **[Listen for Events](/guides/listen-for-events)** — get a webhook the moment a payment is queued, settled, or expires.
* **[Give an Agent a Budget](/guides/agent-budget)** — the daily limits and allowlists a threshold sits alongside.
