Knox
Go To Notes
AI Agents

Who Decides Whether a Mandate Clears?

KnoxKnoxMarch 30, 20265 min read

The mandate was supposed to be the answer. Sign once, delegate intent, let the agent handle the rest. Clean pitch, but the architecture underneath is fragmented.

A mandate does not dissolve trust. It relocates it. It then parcels trust out across credential providers, registries, issuers, and challenge systems, with each one holding a different slice of the acceptance decision.

The user leaves the foreground. Who decides whether the mandate actually clears is a separate problem the protocol does not answer in one place.

AP2 teaches the market how to think about this.[1] User intent becomes a signed mandate. The mandate travels across merchants and infrastructure. The trust problem starts to look solved. Proof replaces presence. Authority becomes portable.

But a cleaner framing sits underneath. The mandate does not close the trust problem. It repacks it and hands it to a different set of actors.

Some prior system has to make the signer, the credential, and the verification path acceptable before the mandate can matter at all. W3C's credential model says the same thing in more general terms: a verifier needs a reason to trust the issuer, and where that trust does not already exist, it has to come from somewhere else.[2]

Agentic payments inherit that structure. The mandate is valid inside a trust framework. Creating the framework is a separate problem the mandate cannot solve by itself.

KNOXHTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Diagram 1</title>
  <style>
    html, body { margin: 0; background: #fff; color: #000; font-family: Inter, "Helvetica Neue", Arial, sans-serif; }
    * { box-sizing: border-box; }
    .diagram { width: 100%; max-width: 1320px; margin: 0 auto; padding: 8px; }
    svg { display: block; width: 100%; height: auto; background: #fff; }
    .frame-label { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; }
    .small-note { font-size: 12px; font-weight: 500; }
    .node-outline { fill: #fff; stroke: #000; stroke-width: 2.2; transition: fill 180ms ease, stroke-width 180ms ease; }
    .node-outline.secondary { stroke-width: 1.9; }
    .node-outline.emphasis { stroke-width: 2.8; }
    .node-label { font-size: 18px; font-weight: 700; pointer-events: none; }
    .node-sub { font-size: 13px; font-weight: 500; pointer-events: none; }
    .connector { fill: none; stroke: #5f5f5f; stroke-width: 1.8; stroke-linecap: round; transition: stroke 180ms ease, stroke-width 180ms ease; }
    .connector.active { stroke: #000; stroke-width: 3.1; }
    .node-group { cursor: pointer; }
    .node-group.active .node-outline, .node-group:hover .node-outline { fill: #f1f1f1; }
    .node-group.active .node-outline.emphasis, .node-group:hover .node-outline.emphasis { fill: #ececec; }
    .node-group.active .node-outline { stroke-width: 3; }
    .icon-stroke { stroke: #000; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; fill: none; pointer-events: none; }
    .icon-fill { fill: #000; pointer-events: none; }
    @media (max-width: 640px) { .diagram { padding: 0; } }
  </style>
</head>
<body>
  <div class="diagram">
    <svg viewBox="30 36 1340 708" aria-labelledby="diagramTitle diagramDesc" role="img">
      <title id="diagramTitle">Diagram 1</title>
      <desc id="diagramDesc">Layered trust map for agentic payments mandates.</desc>
      <rect x="40" y="42" width="1320" height="640" rx="28" ry="28" fill="#fff" stroke="#000" stroke-width="1.7"></rect>
      <text x="74" y="84" class="frame-label">Diagram 1</text>
      <text x="1226" y="84" class="small-note">hover or tap nodes</text>
      <path id="line-signer" class="connector" d="M 420 220 C 500 260, 560 290, 620 320"></path>
      <path id="line-registry" class="connector" d="M 980 140 C 910 200, 850 265, 780 320"></path>
      <path id="line-credential" class="connector active" d="M 980 435 C 900 430, 850 415, 780 400"></path>
      <path id="line-issuer" class="connector" d="M 980 620 C 900 550, 840 480, 780 430"></path>
      <path id="line-challenge" class="connector" d="M 420 540 C 500 500, 560 460, 620 430"></path>
      <g class="node-group active" data-node="mandate">
        <rect class="node-outline emphasis" x="620" y="300" width="160" height="200" rx="22" ry="22"></rect>
        <rect x="650" y="330" width="100" height="128" rx="12" ry="12" fill="#fff" stroke="#000" stroke-width="2"></rect>
        <path class="icon-stroke" d="M 675 362 H 725"></path>
        <path class="icon-stroke" d="M 675 388 H 725"></path>
        <path class="icon-stroke" d="M 675 414 H 710"></path>
        <circle cx="728" cy="414" r="24" fill="#fff" stroke="#000" stroke-width="2"></circle>
        <path class="icon-stroke" d="M 717 414 l7 7 l13 -14"></path>
        <text x="700" y="278" text-anchor="middle" class="frame-label">proof object</text>
        <text x="700" y="534" text-anchor="middle" class="node-label">Mandate</text>
        <text x="700" y="557" text-anchor="middle" class="node-sub">portable authorization</text>
      </g>
      <g class="node-group" data-node="signer">
        <circle class="node-outline secondary" cx="350" cy="190" r="96"></circle>
        <circle cx="350" cy="170" r="23" fill="#fff" stroke="#000" stroke-width="2"></circle>
        <path class="icon-stroke" d="M 316 228 C 323 204, 337 192, 350 192 C 363 192, 377 204, 384 228"></path>
        <path class="icon-stroke" d="M 390 145 l18 -18"></path>
        <path class="icon-stroke" d="M 398 131 h12"></path>
        <path class="icon-stroke" d="M 404 125 v12"></path>
        <text x="350" y="285" text-anchor="middle" class="node-label">Signer</text>
        <text x="350" y="307" text-anchor="middle" class="node-sub">device key</text>
      </g>
      <g class="node-group" data-node="registry">
        <circle class="node-outline secondary" cx="1050" cy="140" r="96"></circle>
        <rect x="1017" y="86" width="66" height="88" rx="10" ry="10" fill="#fff" stroke="#000" stroke-width="2"></rect>
        <path class="icon-stroke" d="M 1031 111 h38"></path>
        <path class="icon-stroke" d="M 1031 133 h38"></path>
        <path class="icon-stroke" d="M 1031 155 h24"></path>
        <circle cx="1065" cy="155" r="10" fill="#fff" stroke="#000" stroke-width="2"></circle>
        <path class="icon-stroke" d="M 1060 155 l3 3 l7 -8"></path>
        <text x="1050" y="235" text-anchor="middle" class="node-label">Registry</text>
        <text x="1050" y="257" text-anchor="middle" class="node-sub">recognition perimeter</text>
      </g>
      <g class="node-group" data-node="credential">
        <rect class="node-outline emphasis" x="960" y="360" width="250" height="150" rx="24" ry="24"></rect>
        <rect x="995" y="400" width="70" height="44" rx="8" ry="8" fill="#fff" stroke="#000" stroke-width="2"></rect>
        <circle cx="1009" cy="422" r="6" class="icon-fill"></circle>
        <path class="icon-stroke" d="M 1023 422 h29"></path>
        <path class="icon-stroke" d="M 1080 378 h48"></path>
        <path class="icon-stroke" d="M 1128 378 l0 56"></path>
        <path class="icon-stroke" d="M 1128 434 h-34"></path>
        <path class="icon-stroke" d="M 1094 434 l0 -18"></path>
        <path class="icon-stroke" d="M 1094 416 h-18"></path>
        <text x="1085" y="345" text-anchor="middle" class="frame-label">execution control point</text>
        <text x="1085" y="540" text-anchor="middle" class="node-label">Credential provider</text>
        <text x="1085" y="562" text-anchor="middle" class="node-sub">scopes usable payment path</text>
      </g>
      <g class="node-group" data-node="challenge">
        <circle class="node-outline secondary" cx="350" cy="540" r="96"></circle>
        <path class="icon-stroke" d="M 324 510 a30 30 0 1 1 52 21 l-14 17 v20"></path>
        <circle cx="350" cy="584" r="4.5" class="icon-fill"></circle>
        <text x="350" y="635" text-anchor="middle" class="node-label">Challenge surface</text>
        <text x="350" y="657" text-anchor="middle" class="node-sub">human step-in</text>
      </g>
      <g class="node-group" data-node="issuer">
        <circle class="node-outline secondary" cx="1050" cy="620" r="96"></circle>
        <rect x="1012" y="588" width="76" height="48" rx="10" ry="10" fill="#fff" stroke="#000" stroke-width="2"></rect>
        <circle cx="1030" cy="612" r="5.5" class="icon-fill"></circle>
        <path class="icon-stroke" d="M 1044 612 h26"></path>
        <path class="icon-stroke" d="M 1098 580 h34"></path>
        <path class="icon-stroke" d="M 1098 600 h34"></path>
        <path class="icon-stroke" d="M 1098 620 h34"></path>
        <text x="1050" y="715" text-anchor="middle" class="node-label">Issuer / network</text>
        <text x="1050" y="737" text-anchor="middle" class="node-sub">rail-level acceptance</text>
      </g>
      <text x="127" y="570" class="small-note">fallback</text>
      <text x="1215" y="650" class="small-note">clear or deny</text>
    </svg>
  </div>
  <script>
    (() => {
      const nodeLines = {
        mandate: ["line-signer", "line-registry", "line-credential", "line-issuer", "line-challenge"],
        signer: ["line-signer"],
        registry: ["line-registry"],
        credential: ["line-credential"],
        issuer: ["line-issuer"],
        challenge: ["line-challenge"]
      };
      const groups = Array.from(document.querySelectorAll(".node-group"));
      function setActive(nodeName) {
        groups.forEach((group) => group.classList.toggle("active", group.dataset.node === nodeName));
        document.querySelectorAll(".connector").forEach((line) => line.classList.remove("active"));
        (nodeLines[nodeName] || []).forEach((id) => document.getElementById(id)?.classList.add("active"));
      }
      groups.forEach((group) => {
        group.tabIndex = 0;
        group.setAttribute("role", "button");
        group.setAttribute("aria-label", `${group.dataset.node} layer`);
        group.addEventListener("mouseenter", () => setActive(group.dataset.node));
        group.addEventListener("focus", () => setActive(group.dataset.node));
        group.addEventListener("click", () => setActive(group.dataset.node));
      });
      setActive("mandate");
    })();
  </script>
</body>
</html>

Diagram 1: The mandate is the visible proof object, but clearance still depends on the surrounding actors that validate identity, recognition, instrument scope, and fallback authentication.

The first dependency shows up at the user edge. Cart Mandates and Intent Mandates are signed by the user, often with a hardware-backed key on the device.[1] That signature proves approval of a specific transaction object. Commercial acceptance requires more.

Someone still has to decide that the key belongs to a legitimate user, that the surrounding identity stack is credible, and that the signature is enough to act on. AP2 leaves that root open. Issuers, networks, governments, merchants, and third parties can all anchor it.[1]

Merchant signatures follow the same logic. AP2 requires the merchant to sign the cart it creates, binding product, price, and shipping terms into an object the user later approves.[1] Disputes get a cleaner record from that step. Performance risk does not disappear with it. A signed cart does not deliver a package or honor a refund. What it gives the system is a sharper liability object when performance eventually breaks.

Control starts to concentrate around the credential provider. This is where broad user authority gets converted into something the rails may actually clear, and it is the part of the stack that deserves more scrutiny than the mandate format itself.[1]

Delegated intent becomes an executable payment route at that layer, not before it. A user can approve categories, spending limits, or merchant types. The credential provider still shapes which instrument becomes live, how narrowly it is scoped, and whether extra friction appears before authorization completes.

Power sits there because execution sits there.

Registry design pushes this dynamic into market structure. AP2's short-term trust model relies on curated allow lists and trusted registries for shopping agents, credential providers, and merchants.[1] Open discovery is not doing the hard work. Recognition is. Access to the trust perimeter determines who gets treated as a legitimate participant.

The credential format cannot do that job alone. Someone still has to decide whose credentials count, whose keys are accepted, and which software is allowed to appear in the transaction flow as a valid actor.

Institutional infrastructure keeps its veto even after the mandate arrives. AP2 allows the PaymentMandate to move to the network and issuer so they can see that agentic activity is happening.[1] Visibility does not equal discretion surrendered. Issuers still approve, decline, or challenge on their own terms.

The Agentic Commerce Protocol makes the same boundary explicit from a different direction: merchants still bring their own PSP, and settlement, refunds, chargebacks, and compliance remain merchant-side responsibilities.[3] Standardized authority sits on top of those rails. Whether money moves is still an institutional decision.

Challenge flows expose the limit of the whole architecture. AP2 allows parties in the stack to invoke mechanisms like 3DS2 when risk rises.[1] Agentic commerce does not remove fallback authentication. It delays it. The human leaves the foreground until the stack decides it wants the human back.

KNOXHTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Diagram 3</title>
  <style>
    html, body { margin: 0; background: #fff; font-family: Inter, "Helvetica Neue", Arial, sans-serif; }
    * { box-sizing: border-box; }
    .diagram { width: 100%; max-width: 1500px; margin: 0 auto; padding: 8px; background: #fff; }
    svg { display: block; width: 100%; height: auto; background: #fff; }
    .node, .branch-hit { cursor: pointer; }
    .node-shape { fill: #fff; stroke: #000; stroke-width: 2; transition: fill 160ms ease, stroke-width 160ms ease, transform 160ms ease; transform-box: fill-box; transform-origin: center; }
    .node.primary .node-shape { stroke-width: 2.8; }
    .node:hover .node-shape, .node.active .node-shape { fill: #f3f3f3; stroke-width: 3.1; transform: translateY(-2px); }
    .node.primary:hover .node-shape, .node.primary.active .node-shape { fill: #ededed; stroke-width: 3.6; }
    .label { fill: #000; font-size: 18px; font-weight: 700; text-anchor: middle; pointer-events: none; }
    .sub { fill: #000; font-size: 13px; font-weight: 500; text-anchor: middle; pointer-events: none; }
    .branch-label { fill: #000; font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; text-anchor: middle; pointer-events: none; }
    .line { fill: none; stroke: #8a8a8a; stroke-width: 2.2; transition: stroke 160ms ease, stroke-width 160ms ease; stroke-linecap: round; stroke-linejoin: round; }
    .line.active { stroke: #000; stroke-width: 3.6; }
    .arrow { fill: #8a8a8a; transition: fill 160ms ease; }
    .arrow.active { fill: #000; }
    .icon { fill: none; stroke: #000; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
    .fill { fill: #000; pointer-events: none; }
    .branch-hit { fill: none; stroke: transparent; stroke-width: 22; }
    @media (max-width: 680px) { .diagram { padding: 0; } }
  </style>
</head>
<body>
  <div class="diagram">
    <svg viewBox="30 100 1440 580" role="img" aria-labelledby="t d">
      <title id="t">Diagram 3</title>
      <desc id="d">Decision tree showing normal agentic flow and challenge escalation.</desc>
      <path id="line-start" class="line active" d="M 250 380 H 470"></path>
      <polygon id="arrow-start" class="arrow active" points="470,380 454,371 454,389"></polygon>
      <path id="line-top-a" class="line active" d="M 690 330 C 805 250, 930 210, 1065 210"></path>
      <polygon id="arrow-top-a" class="arrow active" points="1065,210 1049,201 1049,219"></polygon>
      <path id="line-top-b" class="line active" d="M 1279 210 H 1390"></path>
      <polygon id="arrow-top-b" class="arrow active" points="1390,210 1374,201 1374,219"></polygon>
      <path id="line-bottom-a" class="line active" d="M 690 430 C 805 510, 930 550, 1065 550"></path>
      <polygon id="arrow-bottom-a" class="arrow active" points="1065,550 1049,541 1049,559"></polygon>
      <path id="line-bottom-b" class="line active" d="M 1279 550 H 1390"></path>
      <polygon id="arrow-bottom-b" class="arrow active" points="1390,550 1374,541 1374,559"></polygon>
      <path class="branch-hit" data-branch="normal" d="M 690 330 C 805 250, 930 210, 1065 210"></path>
      <path class="branch-hit" data-branch="normal" d="M 1279 210 H 1390"></path>
      <path class="branch-hit" data-branch="challenge" d="M 690 430 C 805 510, 930 550, 1065 550"></path>
      <path class="branch-hit" data-branch="challenge" d="M 1279 550 H 1390"></path>
      <text x="160" y="132" class="branch-label">Diagram 3</text>
      <text x="900" y="154" class="branch-label">NORMAL FLOW</text>
      <text x="900" y="494" class="branch-label">RISK FLAG</text>
      <g class="node active" data-branch="all">
        <rect class="node-shape" x="70" y="288" width="180" height="184" rx="22" ry="22"></rect>
        <circle cx="160" cy="330" r="24" fill="#fff" stroke="#000" stroke-width="2"></circle>
        <path class="icon" d="M 126 388 C 134 362, 147 348, 160 348 C 173 348, 186 362, 194 388"></path>
        <path class="icon" d="M 204 318 l17 -17"></path>
        <path class="icon" d="M 212 304 h11"></path>
        <path class="icon" d="M 217 299 v11"></path>
        <text x="160" y="414" class="label">Agentic flow</text>
        <text x="160" y="437" class="sub">submitted</text>
      </g>
      <g class="node primary active" data-branch="all">
        <rect class="node-shape" x="470" y="288" width="220" height="184" rx="22" ry="22"></rect>
        <rect x="540" y="322" width="80" height="46" rx="10" ry="10" fill="#fff" stroke="#000" stroke-width="2"></rect>
        <circle cx="558" cy="345" r="5" class="fill"></circle>
        <path class="icon" d="M 571 345 h30"></path>
        <path class="icon" d="M 515 394 h130"></path>
        <path class="icon" d="M 515 414 h92"></path>
        <text x="580" y="414" class="label">Issuer / network</text>
        <text x="580" y="437" class="sub">decision</text>
      </g>
      <g class="node" data-branch="normal">
        <rect class="node-shape" x="1065" y="118" width="214" height="184" rx="22" ry="22"></rect>
        <circle cx="1172" cy="171" r="30" fill="#fff" stroke="#000" stroke-width="2"></circle>
        <path class="icon" d="M 1158 171 l9 9 l20 -21"></path>
        <path class="icon" d="M 1120 226 h104"></path>
        <text x="1172" y="244" class="label">Approve</text>
        <text x="1172" y="267" class="sub">clearance continues</text>
      </g>
      <g class="node" data-branch="normal">
        <rect class="node-shape" x="1176" y="118" width="214" height="184" rx="22" ry="22"></rect>
        <rect x="1242" y="150" width="58" height="36" rx="8" ry="8" fill="#fff" stroke="#000" stroke-width="2"></rect>
        <circle cx="1255" cy="168" r="4.5" class="fill"></circle>
        <path class="icon" d="M 1267 168 h23"></path>
        <path class="icon" d="M 1323 157 h40"></path>
        <path class="icon" d="M 1323 177 h40"></path>
        <path class="icon" d="M 1323 197 h40"></path>
        <text x="1283" y="244" class="label">Normal flow</text>
        <text x="1283" y="267" class="sub">payment proceeds</text>
      </g>
      <g class="node" data-branch="challenge">
        <rect class="node-shape" x="1065" y="458" width="214" height="184" rx="22" ry="22"></rect>
        <path class="icon" d="M 1138 531 a34 34 0 1 1 58 23 l-16 20 v18"></path>
        <circle cx="1172" cy="597" r="4.5" class="fill"></circle>
        <text x="1172" y="584" class="label">3DS2 / challenge</text>
        <text x="1172" y="607" class="sub">step-up authentication</text>
      </g>
      <g class="node" data-branch="challenge">
        <rect class="node-shape" x="1176" y="458" width="214" height="184" rx="22" ry="22"></rect>
        <circle cx="1283" cy="500" r="24" fill="#fff" stroke="#000" stroke-width="2"></circle>
        <path class="icon" d="M 1249 556 C 1257 530, 1270 517, 1283 517 C 1296 517, 1309 530, 1317 556"></path>
        <text x="1283" y="584" class="label">Human step-in</text>
        <text x="1283" y="607" class="sub">manual verification</text>
      </g>
    </svg>
  </div>
  <script>
    (() => {
      const nodes = Array.from(document.querySelectorAll(".node"));
      const hits = Array.from(document.querySelectorAll(".branch-hit"));
      const branchMap = {
        all: ["line-start", "arrow-start", "line-top-a", "arrow-top-a", "line-top-b", "arrow-top-b", "line-bottom-a", "arrow-bottom-a", "line-bottom-b", "arrow-bottom-b"],
        normal: ["line-start", "arrow-start", "line-top-a", "arrow-top-a", "line-top-b", "arrow-top-b"],
        challenge: ["line-start", "arrow-start", "line-bottom-a", "arrow-bottom-a", "line-bottom-b", "arrow-bottom-b"]
      };
      const lineIds = ["line-start", "line-top-a", "line-top-b", "line-bottom-a", "line-bottom-b"];
      const arrowIds = ["arrow-start", "arrow-top-a", "arrow-top-b", "arrow-bottom-a", "arrow-bottom-b"];
      function clearActive() {
        nodes.forEach((node) => node.classList.remove("active"));
        lineIds.forEach((id) => document.getElementById(id)?.classList.remove("active"));
        arrowIds.forEach((id) => document.getElementById(id)?.classList.remove("active"));
      }
      function setBranch(branch) {
        clearActive();
        (branchMap[branch] || []).forEach((id) => document.getElementById(id)?.classList.add("active"));
        nodes.forEach((node) => {
          if (branch === "all") {
            if (node.dataset.branch === "all") node.classList.add("active");
            return;
          }
          if (node.dataset.branch === "all" || node.dataset.branch === branch) node.classList.add("active");
        });
      }
      nodes.forEach((node) => {
        node.tabIndex = 0;
        node.setAttribute("role", "button");
        node.addEventListener("mouseenter", () => setBranch(node.dataset.branch));
        node.addEventListener("focus", () => setBranch(node.dataset.branch));
        node.addEventListener("click", () => setBranch(node.dataset.branch));
      });
      hits.forEach((hit) => {
        hit.addEventListener("mouseenter", () => setBranch(hit.dataset.branch));
        hit.addEventListener("click", () => setBranch(hit.dataset.branch));
      });
      document.body.addEventListener("mouseleave", () => setBranch("all"));
      setBranch("all");
    })();
  </script>
</body>
</html>

Diagram 3: Agentic flow can feel autonomous all the way up to the issuer decision point, but the stack can still split into a normal approval path or a human challenge path.

Market power will not come from owning a mandate format. The mandate is table stakes.

Control gathers around the points where acceptance gets decided, where credentials become usable, and where delegated authority converts into a payment instrument the network will honor. Credential-provider distribution sits closest to execution and therefore carries the most pricing power. Registry access matters because it determines who enters the trust perimeter at all. Issuer approval and challenge routing shape the boundary conditions around both.

Clean proof objects help. Clearance is what the whole stack is actually selling, and the layers closest to clearance are where value accumulates.

The interface got abstracted away. The trust stack did not.

A user can now delegate authority upstream and carry it downstream as an attested object. That is real progress on visibility and scope control. But execution still depends on whether the merchant, PSP, or issuer decides that proof is sufficient under its own policy. The mandate travels farther than the old checkout click. Liability does not travel with it.

The market is not moving from trust to trustlessness. It is moving from implicit consent to explicit trust routing. The attestation layer is converging faster than the resolution layer because permission is simpler than liability. And that gap between what standardizes cleanly and what remains institutional is exactly where the next power structure will form.

Sources

Linked sources

Loading engagement...

Comments

Reader discussion and protocol-level debate.

Loading comments...

No comments yet.