Skip to main content

Scenario

An insurance claims processing system receives claim submissions. Before paying out a claim, every decision approve, reject, or escalate for manual review must be governed, signed, and auditable. Requirements:
  • Decisions must be reproducible from the original claim data
  • Any claim can be re-verified against the original policy months after processing
  • Manual review cases must follow an override approval workflow
  • Regulators can audit any claim without system access

Policy

Create policies/claims-approval/1.0.0/policy.json:
{
  "policyId": "claims-approval",
  "policyVersion": "1.0.0",
  "schemaVersion": "1.0.0",
  "signalsSchema": {
    "claimAmount":        { "type": "number" },
    "customerTier":       { "type": "string" },
    "priorFraudSignals":  { "type": "boolean" },
    "claimType":          { "type": "string" },
    "daysSinceIncident":  { "type": "integer" }
  },
  "rules": [
    {
      "id": "reject-fraud",
      "condition": { "signal": "priorFraudSignals", "equals": true },
      "outcome": {
        "action": "reject",
        "requires_override": false,
        "reason": "Claim rejected: prior fraud signals on account."
      }
    },
    {
      "id": "auto-approve-small",
      "condition": {
        "all": [
          { "signal": "claimAmount", "less_than": 500 },
          { "signal": "priorFraudSignals", "equals": false }
        ]
      },
      "outcome": {
        "action": "approve",
        "requires_override": false,
        "reason": "Auto-approved: low-value claim, no fraud signals."
      }
    },
    {
      "id": "gold-tier-threshold",
      "condition": {
        "all": [
          { "signal": "customerTier", "equals": "gold" },
          { "signal": "claimAmount", "less_than": 5000 }
        ]
      },
      "outcome": {
        "action": "approve",
        "requires_override": false,
        "reason": "Approved: gold tier within standard limit."
      }
    },
    {
      "id": "large-claim-review",
      "condition": { "signal": "claimAmount", "greater_than_or_equal": 5000 },
      "outcome": {
        "action": "manual_review",
        "requires_override": true,
        "reason": "Claim requires senior adjuster review: amount exceeds auto-approval threshold."
      }
    },
    {
      "id": "standard-approval",
      "condition": { "all": [] },
      "outcome": {
        "action": "approve",
        "requires_override": false,
        "reason": "Standard approval."
      }
    }
  ]
}

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 ClaimSignals {
  claimAmount: number;
  customerTier: "standard" | "gold" | "platinum";
  priorFraudSignals: boolean;
  claimType: string;
  daysSinceIncident: number;
}

interface ClaimDecision {
  claimId: string;
  action: string;
  reason: string;
  requiresOverride: boolean;
  attestationSignature: string;
  executionFingerprint: string;
}

async function governClaimDecision(
  claimId: string,
  signals: ClaimSignals
): Promise<ClaimDecision> {
  let attestation: ExecutionAttestation;

  try {
    attestation = await client.execute({
      executionId: `claim-${claimId}`,
      policyId: "claims-approval",
      policyVersion: "1.0.0",
      signals,
    });
  } catch (err) {
    if (err instanceof ParmanaApiError && err.status === 422) {
      // Check if this is a replay — the claim may already have been processed
      const existing = await client.audit.decision(`claim-${claimId}`);
      if (existing) {
        return {
          claimId,
          action: existing.decision,
          reason: "Previously verified authority decision retrieved from audit",
          requiresOverride: false,
          attestationSignature: existing.attestation?.signature ?? "",
          executionFingerprint: existing.execution_fingerprint,
        };
      }
    }
    throw err;
  }

  // Verify immediately after execution
  const verification = await client.verify(attestation);
  if (!verification.valid) {
    throw new Error(
      `Attestation verification failed for claim ${claimId}. ` +
      `Do not proceed with this decision.`
    );
  }

  return {
    claimId,
    action: attestation.decision.action,
    reason: attestation.decision.reason,
    requiresOverride: attestation.decision.requires_override,
    attestationSignature: attestation.signature,
    executionFingerprint: attestation.execution_fingerprint,
  };
}

// Example usage
async function main() {
  // Auto-approved: gold tier, low value
  const approved = await governClaimDecision("CLM-2024-00441", {
    claimAmount: 1200,
    customerTier: "gold",
    priorFraudSignals: false,
    claimType: "property_damage",
    daysSinceIncident: 3,
  });
  console.log(approved.action);           // "approve"
  console.log(approved.requiresOverride); // false

  // Rejected: fraud signal
  const rejected = await governClaimDecision("CLM-2024-00442", {
    claimAmount: 800,
    customerTier: "standard",
    priorFraudSignals: true,
    claimType: "theft",
    daysSinceIncident: 7,
  });
  console.log(rejected.action);   // "reject"

  // Pending override: large claim
  const pending = await governClaimDecision("CLM-2024-00443", {
    claimAmount: 12000,
    customerTier: "standard",
    priorFraudSignals: false,
    claimType: "medical",
    daysSinceIncident: 14,
  });
  console.log(pending.action);           // "manual_review"
  console.log(pending.requiresOverride); // true
}

main().catch(console.error);

Handling pending override

When requiresOverride is true, store the claim ID and route it to your adjuster queue. When the adjuster approves:
// Approve the override
const approval = await client._request<{
  status: string;
  overrideId: string;
}>("/override", {
  method: "POST",
  body: JSON.stringify({
    executionId: `claim-${claimId}`,
    approved: true,
    approvedBy: "adjuster-sarah-chen",
    approverRole: "senior_adjuster",
    reason: "Verified documentation supports claim. Approved for payment.",
  }),
});

console.log(approval.status);     // "approved"
console.log(approval.overrideId); // cryptographic override record ID

// To reject:
const rejection = await client._request<{
  status: string;
  executionId: string;
}>("/override", {
  method: "POST",
  body: JSON.stringify({
    executionId: `claim-${claimId}`,
    approved: false,
    approvedBy: "adjuster-sarah-chen",
    approverRole: "senior_adjuster",
    reason: "Documentation insufficient. Claim rejected.",
  }),
});

console.log(rejection.status); // "rejected"

Expected results

InputExpected actionrequires_override
claimAmount: 300, priorFraudSignals: falseapprovefalse
claimAmount: 2000, customerTier: "gold"approvefalse
priorFraudSignals: truerejectfalse
claimAmount: 12000manual_reviewtrue

Audit lookup

const record = await client.audit.decision("claim-CLM-2024-00441");

console.log(record.decision);           // "approve"
console.log(record.policy_id);          // "claims-approval"
console.log(record.policy_version);     // "1.0.0"
console.log(record.executed_at);        // ISO timestamp
console.log(record.signature_verified); // "verified"

Troubleshooting

[INV-013@replay] Replay detected A claim with this executionId was already processed. Each claim submission must use a unique executionId. Use the claim’s canonical ID from your system (e.g., claim-CLM-2024-00441). signals validation failed The signals object does not match the policy’s signalsSchema. Ensure all required signal fields are present and correctly typed. execution_state: "pending_override" but override route returns 404 The override route requires approved, approvedBy, approverRole, and reason. All are required. Also verify that audit_db: true in /health — without Postgres, pending override state cannot be stored.