Skip to main content

Scenario

An AI agent wants to send money. The agent identifies a recipient. The agent determines an amount. The agent prepares a payment request. The agent can generate intent. The agent does not possess authority. Authority is defined by humans through policy. Before money moves, Parmana verifies that the requested payment complies with human-defined authority requirements. If authority is verified:
  • A signed attestation is issued
  • The payment may proceed
  • The decision can be independently verified later
If authority is not verified:
  • The payment is blocked
  • No money moves
  • The failure is auditable
The payment processor does not trust the AI agent. The payment processor trusts a valid authority verification attestation. This example demonstrates the core Parmana principle: AI Has Intelligence. Humans Have Authority. Parmana Verifies Authority Before Execution.

Policy

Create policies/payment-approval/1.0.0/policy.json:
{
  "policyId": "payment-approval",
  "policyVersion": "1.0.0",
  "schemaVersion": "1.0.0",
  "signalsSchema": {
    "amount":              { "type": "number" },
    "currency":            { "type": "string" },
    "merchantCategory":    { "type": "string" },
    "accountAge":          { "type": "integer" },
    "fraudScore":          { "type": "number" },
    "velocityCount24h":    { "type": "integer" },
    "countryRisk":         { "type": "string" }
  },
  "rules": [
    {
      "id": "block-high-fraud",
      "condition": { "signal": "fraudScore", "greater_than": 0.8 },
      "outcome": {
        "action": "reject",
        "requires_override": false,
        "reason": "Transaction blocked: fraud score exceeds maximum threshold."
      }
    },
    {
      "id": "block-high-velocity",
      "condition": { "signal": "velocityCount24h", "greater_than": 20 },
      "outcome": {
        "action": "reject",
        "requires_override": false,
        "reason": "Transaction blocked: velocity limit exceeded."
      }
    },
    {
      "id": "hold-large-transaction",
      "condition": { "signal": "amount", "greater_than": 10000 },
      "outcome": {
        "action": "hold",
        "requires_override": true,
        "reason": "Transaction held for manual review: amount exceeds auto-approval limit."
      }
    },
    {
      "id": "hold-high-risk-country",
      "condition": { "signal": "countryRisk", "equals": "HIGH" },
      "outcome": {
        "action": "hold",
        "requires_override": true,
        "reason": "Transaction held: high-risk country of origin."
      }
    },
    {
      "id": "approve-standard",
      "condition": { "all": [] },
      "outcome": {
        "action": "approve",
        "requires_override": false,
        "reason": "Transaction approved: within standard parameters."
      }
    }
  ]
}

Complete TypeScript example

import { ParmanaClient, ParmanaApiError } from "@parmanasystems/sdk-client";
import type { ExecutionAttestation } from "@parmanasystems/sdk-client";

const client = new ParmanaClient({
  baseUrl: process.env.PARMANA_URL ?? "http://localhost:3000",
  apiKey: process.env.PARMANA_API_KEY,
});

interface PaymentSignals {
  amount: number;
  currency: string;
  merchantCategory: string;
  accountAge: number;      // days
  fraudScore: number;      // 0.0–1.0
  velocityCount24h: number;
  countryRisk: "LOW" | "MEDIUM" | "HIGH";
}

interface PaymentAuthResult {
  transactionId: string;
  authorized: boolean;
  action: string;
  reason: string;
  requiresHold: boolean;
  attestationSignature: string;
}

async function authorizePayment(
  transactionId: string,
  signals: PaymentSignals
): Promise<PaymentAuthResult> {
  const attestation = await client.execute({
    executionId: transactionId,  // transaction ID IS the executionId — must be globally unique
    policyId: "payment-approval",
    policyVersion: "1.0.0",
    signals,
  });

  // Always verify before authorizing — fail closed
  const verification = await client.verify(attestation);
  if (!verification.valid) {
    return {
      transactionId,
      authorized: false,
      action: "reject",
      reason: "Governance attestation could not be verified. Payment blocked.",
      requiresHold: false,
      attestationSignature: "",
    };
  }

  const authorized =
    attestation.execution_state === "completed" &&
    attestation.decision.action === "approve";

  return {
    transactionId,
    authorized,
    action: attestation.decision.action,
    reason: attestation.decision.reason,
    requiresHold: attestation.execution_state === "pending_override",
    attestationSignature: attestation.signature,
  };
}

// Post-execution confirmation — prove the payment matched the authorization
async function confirmPaymentExecuted(
  transactionId: string,
  attestation: ExecutionAttestation,
  paymentDetails: {
    amount: number;
    currency: string;
    recipientAccountId: string;
  }
) {
  const proof = await client.confirmExecution({
    attestation,
    executedAction: {
      actionType: "payment_transfer",
      actionId: transactionId,
      actionTimestamp: new Date().toISOString(),
      actionDetails: paymentDetails,
    },
    timeWindowSeconds: 60,  // action must occur within 60 seconds of authorization
  });

  return proof;
}

// Example usage
async function main() {
  // Standard approval
  const approved = await authorizePayment("TXN-20240115-0001", {
    amount: 250,
    currency: "USD",
    merchantCategory: "retail",
    accountAge: 730,
    fraudScore: 0.02,
    velocityCount24h: 3,
    countryRisk: "LOW",
  });
  console.log(approved.action);     // "approve"
  console.log(approved.authorized); // true

  // Rejected: fraud score
  const blocked = await authorizePayment("TXN-20240115-0002", {
    amount: 500,
    currency: "USD",
    merchantCategory: "electronics",
    accountAge: 90,
    fraudScore: 0.92,
    velocityCount24h: 15,
    countryRisk: "LOW",
  });
  console.log(blocked.action);     // "reject"
  console.log(blocked.authorized); // false

  // Hold: large transaction
  const held = await authorizePayment("TXN-20240115-0003", {
    amount: 15000,
    currency: "USD",
    merchantCategory: "wire_transfer",
    accountAge: 1200,
    fraudScore: 0.05,
    velocityCount24h: 1,
    countryRisk: "LOW",
  });
  console.log(held.action);       // "hold"
  console.log(held.requiresHold); // true
}

main().catch(console.error);

Handling held transactions

When requiresHold is true, route the transaction to a compliance officer:
const resolution = await client._request<{
  status: string;
  overrideId: string;
}>("/override", {
  method: "POST",
  body: JSON.stringify({
    executionId: "TXN-20240115-0003",
    approved: true,
    approvedBy: "compliance-officer-james-wu",
    approverRole: "compliance_officer",
    reason: "Wire transfer verified: known business account, documented purpose.",
  }),
});

console.log(resolution.status);     // "approved"
console.log(resolution.overrideId); // ID of the override record

Expected results

SignalsExpected actionrequires_override
fraudScore: 0.02, amount: 250approvefalse
fraudScore: 0.92rejectfalse
velocityCount24h: 25rejectfalse
amount: 15000holdtrue
countryRisk: "HIGH"holdtrue

Troubleshooting

[INV-013@replay] Replay detected Each transactionId is used as the executionId. Once a transaction is authorized, it cannot be re-authorized with the same ID. Generate a new transaction ID for retries. execution_state: "pending_override" but action: "approve" A policy rule can set action: "approve" with requires_override: true. The convention is to use a distinct action name (e.g., "hold") when override is required, but it is not enforced. Always check both execution_state and decision.action. Payment executed without attestation verification Always call client.verify() before authorizing a transfer. If verification fails, block the payment and investigate.