# x402: ask before your agent pays

[x402](https://www.x402.org/) servers answer `HTTP 402 Payment Required` with machine-readable payment terms (`accepts`: scheme, network, amount, asset, payTo). An agent with a wallet could pay automatically. KarmaDue's position is that a payment always goes to a person. **KarmaDue never pays, holds funds or signs payments**, and it charges nothing for this check.

What KarmaDue adds: one call, `preflight {target: <resource URL>, action: "pay"}`. It returns `require_human_approval` with a plain headline. If KarmaDue has a malicious or refused record for the target, it returns `block`, which means decline without asking.

Tested: 2026-11-12. A local mock server returned an x402-shaped 402. The wrapper below called KarmaDue live and returned:

```json
{
  "decision": "ask_your_person",
  "karmadue": {
    "enforce": "require_human_approval",
    "headline": "Ask your person before you pay (target: http://127.0.0.1:54402/weather): it moves money.",
    "request_id": "7f0a8f13-b3e5-4f03-bbb6-fc1d48a88483"
  },
  "terms": [{"scheme": "exact", "network": "base-sepolia", "amount": "10000", "asset": "0x036C...", "pay_to": "0x...dEaD", "resource": "http://127.0.0.1:54402/weather", "description": "Weather (test)"}]
}
```

A normal 200 response passed through untouched.

## `kd-x402-guard.mjs` (Node 18+, no dependencies)

```js
// KarmaDue x402 awareness: when a server answers HTTP 402 with x402 payment requirements, do not pay automatically.
// Ask KarmaDue about the resource (action "pay"), then hand the decision and the exact payment terms to a person.
// KarmaDue never pays, holds funds or signs payments. Node 18+, no dependencies.
const KD = process.env.KARMADUE_PREFLIGHT || "https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/public-api/v1/preflight";
export async function fetchWithX402Guard(url, init = {}) {
  const res = await fetch(url, init);
  if (res.status !== 402) return { res };
  let terms = null;
  try { const b = await res.clone().json(); terms = Array.isArray(b?.accepts) ? b.accepts : null; } catch {}
  if (!terms) return { res };                          // a 402 that is not x402: leave it alone
  let kd = { enforce: "require_human_approval", headline: "KarmaDue check failed; not assessed." }, rid = "";
  try {
    const r = await fetch(KD, { method: "POST", headers: { "content-type": "application/json", "x-kd-via": "recipe-x402" }, body: JSON.stringify({ target: String(url), action: "pay" }), signal: AbortSignal.timeout(15000) });
    const b = await r.json(); kd = b.data || kd; rid = b.request_id || "";
  } catch {}
  // Whatever KarmaDue says, a payment always goes to a person. block means: do not even ask, decline.
  return {
    res,
    payment_required: {
      decision: kd.enforce === "block" ? "decline" : "ask_your_person",
      karmadue: { enforce: kd.enforce, headline: kd.headline, request_id: rid },
      terms: terms.map((t) => ({ scheme: t.scheme, network: t.network, amount: t.maxAmountRequired ?? t.amount, asset: t.asset, pay_to: t.payTo, resource: t.resource, description: t.description })),
    },
  };
}
if (import.meta.url === `file://${process.argv[1]}`) {
  const out = await fetchWithX402Guard(process.argv[2]);
  console.log(JSON.stringify(out.payment_required ?? { status: out.res.status }, null, 2));
}
```

Show `payment_required.terms` to your person exactly as the server sent them: amount, asset, network and recipient. Pay only after they approve. KarmaDue's answer is about the target, not about the price.
