Build Multi-Agent Systems for Transaction Dispute Resolution

This tutorial shows you how to build a transaction dispute resolution system using multi-agent architecture, secure data handling, and execution monitoring.

The Agentic Data Plane is supported on BYOC clusters running with AWS and Redpanda version 25.3 and later.

After completing this tutorial, you will be able to:

  • Design multi-agent systems with domain-specific sub-agents

  • Monitor multi-agent execution using Transcripts

  • Integrate agents with streaming pipelines for event-driven processing

What you’ll learn

This tutorial advances from basic multi-tool orchestration to multi-agent systems. You’ll build a transaction dispute resolution system where a root agent delegates to specialized sub-agents (account, fraud, merchant, compliance), each with focused responsibilities and PII-protected data access. You’ll also monitor execution using transcripts and process disputes from transaction streams for automated detection.

These patterns apply beyond banking to any domain requiring specialized expertise and data security: healthcare systems, insurance claims processing, or regulatory compliance workflows.

The scenario

Banks handle thousands of dispute calls daily. Customers report unauthorized charges, billing errors, or unrecognized transactions. Each investigation requires cross-referencing multiple systems and applying consistent fraud detection logic.

Traditionally, human agents manually open multiple systems, cross-reference data, and take notes. A 10-15 minute process prone to inconsistencies and incomplete compliance logging.

Multi-agent automation transforms this workflow by enabling instant data aggregation from all sources, consistent logic applied every time, 10-15 second resolution, and structured results for compliance. Human agents handle only complex escalations.

When a customer calls saying "I see a $247.83 charge from 'ACME CORP' but I never shopped there. Is this fraud?", the system must investigate account history, calculate fraud scores, verify merchant legitimacy, and make a recommendation with structured results.

Prerequisites

Create MCP tools for each domain

Before creating agents, create the tools they’ll use. You’ll organize tools by domain, matching each sub-agent’s responsibility.

Account tools

Account tools retrieve customer and transaction data with PII protection.

  1. Navigate to your cluster in the Redpanda Cloud Console.

  2. Go to Agentic AI > Remote MCP.

  3. Click Create MCP Server.

  4. Configure the server:

    • Name: account-tools

    • Description: Customer account and transaction data retrieval

    • Resource Tier: XSmall

  5. Add the following tools. For each tool, select Processor from the component type dropdown, then click Lint to validate:

    • get_customer_account

    • get_transaction_details

    • get_transaction_history

    This mock tool returns account data with sensitive fields already protected. Card numbers only include the last 4 digits, while full names remain for verification. In production, implement similar protections in your data layer.

    label: get_customer_account
    mapping: |
      root = match {
        this.customer_id == "CUST-1001" => {
          "customer_id": "CUST-1001",
          "name": "Dana A.",
          "email": "s****@example.com",
          "account_type": "premium_checking",
          "card_last_four": "4532",
          "card_status": "active",
          "member_since": "2019-03-15",
          "location": "Seattle, WA, USA",
          "phone_masked": "***-***-7890"
        },
        this.customer_id == "CUST-1002" => {
          "customer_id": "CUST-1002",
          "name": "Alex T.",
          "email": "m****@example.com",
          "account_type": "standard_checking",
          "card_last_four": "8821",
          "card_status": "active",
          "member_since": "2021-07-22",
          "location": "San Francisco, CA, USA",
          "phone_masked": "***-***-4521"
        },
        this.customer_id == "CUST-1003" => {
          "customer_id": "CUST-1003",
          "name": "Quinn N.",
          "email": "e****@example.com",
          "account_type": "premium_credit",
          "card_last_four": "2193",
          "card_status": "active",
          "member_since": "2020-11-08",
          "location": "Austin, TX, USA",
          "phone_masked": "***-***-3344"
        },
        _ => {
          "error": "customer_not_found",
          "message": "No account found for customer ID: " + this.customer_id
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Retrieve customer account information with masked PII. Use CUST-1001, CUST-1002, or CUST-1003 for testing."
        properties:
          - name: customer_id
            type: string
            description: "Customer identifier (format CUST-XXXX)"
            required: true

    This tool returns complete transaction details including merchant information, location, and timestamp. Notice how it returns structured data the fraud agent can analyze.

    label: get_transaction_details
    mapping: |
      root = match {
        this.transaction_id == "TXN-89012" => {
          "transaction_id": "TXN-89012",
          "customer_id": "CUST-1001",
          "amount": 1847.99,
          "currency": "USD",
          "merchant": {
            "name": "LUXURY WATCHES INT",
            "category": "jewelry",
            "country": "SG",
            "mcc": "5944"
          },
          "card_last_four": "4532",
          "date": "2026-01-18T14:22:00Z",
          "location": {
            "city": "Singapore",
            "country": "SG",
            "coordinates": "1.3521,103.8198"
          },
          "status": "posted"
        },
        this.transaction_id == "TXN-89013" => {
          "transaction_id": "TXN-89013",
          "customer_id": "CUST-1001",
          "amount": 47.83,
          "currency": "USD",
          "merchant": {
            "name": "EXAMPLE MKTPLACE",
            "category": "online_retail",
            "country": "US",
            "mcc": "5942"
          },
          "card_last_four": "4532",
          "date": "2026-01-15T10:15:00Z",
          "location": {
            "city": "Seattle",
            "country": "US",
            "coordinates": "47.6062,-122.3321"
          },
          "status": "posted"
        },
        this.transaction_id == "TXN-89014" => {
          "transaction_id": "TXN-89014",
          "customer_id": "CUST-1002",
          "amount": 29.99,
          "currency": "USD",
          "merchant": {
            "name": "EXAMPLE STREAMING",
            "category": "subscription_service",
            "country": "US",
            "mcc": "4899"
          },
          "card_last_four": "8821",
          "date": "2025-12-15T00:00:01Z",
          "location": {
            "city": "San Francisco",
            "country": "US",
            "coordinates": "37.7749,-122.4194"
          },
          "status": "posted",
          "recurring": true
        },
        this.transaction_id == "TXN-89015" => {
          "transaction_id": "TXN-89015",
          "customer_id": "CUST-1003",
          "amount": 312.50,
          "currency": "EUR",
          "merchant": {
            "name": "HOTEL PARIS",
            "category": "lodging",
            "country": "FR",
            "mcc": "7011"
          },
          "card_last_four": "2193",
          "date": "2026-01-10T20:30:00Z",
          "location": {
            "city": "Paris",
            "country": "FR",
            "coordinates": "48.8566,2.3522"
          },
          "status": "posted"
        },
        _ => {
          "error": "transaction_not_found",
          "message": "No transaction found with ID: " + this.transaction_id
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Retrieve detailed transaction information including merchant, location, and amount. Use TXN-89012 through TXN-89015 for testing."
        properties:
          - name: transaction_id
            type: string
            description: "Transaction identifier (format TXN-XXXXX)"
            required: true

    This tool returns aggregated spending patterns instead of raw transaction lists. This privacy-preserving approach gives fraud analysis what it needs (typical spending by category, location patterns) without exposing individual transaction details unnecessarily.

    label: get_transaction_history
    mapping: |
      root = match {
        this.customer_id == "CUST-1001" => {
          "customer_id": "CUST-1001",
          "analysis_period": "last_90_days",
          "spending_patterns": {
            "average_transaction": 127.45,
            "median_transaction": 65.20,
            "total_transactions": 87,
            "total_amount": 11088.15
          },
          "category_breakdown": [
            {"category": "online_retail", "count": 42, "avg_amount": 78.50},
            {"category": "groceries", "count": 28, "avg_amount": 95.30},
            {"category": "restaurants", "count": 12, "avg_amount": 45.80},
            {"category": "gas_stations", "count": 5, "avg_amount": 62.00}
          ],
          "location_patterns": {
            "primary_region": "US_West_Coast",
            "international_transactions": 0,
            "cities": ["Seattle", "Bellevue", "Tacoma"]
          },
          "merchant_patterns": {
            "recurring_merchants": ["EXAMPLE MKTPLACE", "EXAMPLE WHOLESALE", "EXAMPLE COFFEE"],
            "first_time_merchants_this_period": 3
          },
          "risk_indicators": {
            "unusual_activity": false,
            "velocity_flags": 0,
            "declined_transactions": 1
          }
        },
        this.customer_id == "CUST-1002" => {
          "customer_id": "CUST-1002",
          "analysis_period": "last_90_days",
          "spending_patterns": {
            "average_transaction": 95.33,
            "median_transaction": 52.10,
            "total_transactions": 64,
            "total_amount": 6101.12
          },
          "category_breakdown": [
            {"category": "subscription_service", "count": 15, "avg_amount": 29.99},
            {"category": "restaurants", "count": 25, "avg_amount": 68.40},
            {"category": "online_retail", "count": 18, "avg_amount": 110.20},
            {"category": "entertainment", "count": 6, "avg_amount": 45.00}
          ],
          "location_patterns": {
            "primary_region": "US_West_Coast",
            "international_transactions": 0,
            "cities": ["San Francisco", "Oakland", "San Jose"]
          },
          "merchant_patterns": {
            "recurring_merchants": ["EXAMPLE STREAMING", "EXAMPLE MEDIA", "EXAMPLE AUDIO"],
            "first_time_merchants_this_period": 7
          },
          "risk_indicators": {
            "unusual_activity": false,
            "velocity_flags": 0,
            "declined_transactions": 0
          }
        },
        this.customer_id == "CUST-1003" => {
          "customer_id": "CUST-1003",
          "analysis_period": "last_90_days",
          "spending_patterns": {
            "average_transaction": 215.67,
            "median_transaction": 145.00,
            "total_transactions": 52,
            "total_amount": 11214.84
          },
          "category_breakdown": [
            {"category": "travel", "count": 8, "avg_amount": 650.00},
            {"category": "lodging", "count": 6, "avg_amount": 380.50},
            {"category": "restaurants", "count": 22, "avg_amount": 85.20},
            {"category": "online_retail", "count": 16, "avg_amount": 95.75}
          ],
          "location_patterns": {
            "primary_region": "US_South",
            "international_transactions": 3,
            "cities": ["Austin", "Houston", "Dallas", "Paris", "London"]
          },
          "merchant_patterns": {
            "recurring_merchants": ["EXAMPLE AIRLINES", "EXAMPLE HOTEL", "EXAMPLE TRAVEL"],
            "first_time_merchants_this_period": 12
          },
          "risk_indicators": {
            "unusual_activity": false,
            "velocity_flags": 0,
            "declined_transactions": 0
          }
        },
        _ => {
          "error": "customer_not_found",
          "message": "No transaction history found for customer ID: " + this.customer_id
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Retrieve customer transaction history with spending patterns, category breakdown, and risk indicators. Use CUST-1001, CUST-1002, or CUST-1003 for testing."
        properties:
          - name: customer_id
            type: string
            description: "Customer identifier (format CUST-XXXX)"
            required: true
  6. Click Create MCP Server.

Wait for the server status to show Running.

This tutorial uses XSmall resource tier for all MCP servers because the mock tools run lightweight Bloblang transformations. Production deployments with external API calls require larger tiers based on throughput needs. See Scale Remote MCP Server Resources.

Fraud tools

Fraud tools calculate risk scores and identify fraud indicators.

  1. Click Create MCP Server.

  2. Configure the server:

    • Name: fraud-tools

    • Description: Fraud detection and risk scoring

    • Resource Tier: XSmall

  3. Add the following tools. For each tool, select Processor from the component type dropdown, then click Lint to validate:

    • calculate_fraud_score

    • get_risk_indicators

    This tool implements multi-factor fraud scoring with location risk (0-35 for international/unusual cities), merchant risk (0-30 for reputation/fraud reports), amount risk (0-25 for deviation from averages), velocity risk (0-15 for rapid transactions), and category risk (0-20 for unusual spending categories). The tool returns both the total score and breakdown, allowing agents to explain their reasoning.

    label: calculate_fraud_score
    mapping: |
      root = match {
        this.transaction_id == "TXN-89012" && this.customer_id == "CUST-1001" => {
          "transaction_id": "TXN-89012",
          "customer_id": "CUST-1001",
          "fraud_score": 95,
          "risk_level": "critical",
          "score_breakdown": {
            "location_risk": 35,
            "merchant_risk": 30,
            "amount_risk": 25,
            "velocity_risk": 0,
            "category_risk": 20
          },
          "factors_detected": [
            "unusual_location",
            "questionable_merchant",
            "unusual_amount",
            "unusual_category"
          ],
          "reasoning": "International transaction from Singapore with no customer history of international purchases. High-value jewelry purchase (14.5x customer average). Merchant has significant fraud indicators.",
          "recommendation": "block_and_investigate"
        },
        this.transaction_id == "TXN-89013" && this.customer_id == "CUST-1001" => {
          "transaction_id": "TXN-89013",
          "customer_id": "CUST-1001",
          "fraud_score": 8,
          "risk_level": "minimal",
          "score_breakdown": {
            "location_risk": 0,
            "merchant_risk": 0,
            "amount_risk": 0,
            "velocity_risk": 0,
            "category_risk": 0
          },
          "factors_detected": [],
          "reasoning": "Local transaction from trusted merchant in customer's typical spending category and amount range.",
          "recommendation": "approve"
        },
        this.transaction_id == "TXN-89014" && this.customer_id == "CUST-1002" => {
          "transaction_id": "TXN-89014",
          "customer_id": "CUST-1002",
          "fraud_score": 52,
          "risk_level": "medium",
          "score_breakdown": {
            "location_risk": 0,
            "merchant_risk": 15,
            "amount_risk": 0,
            "velocity_risk": 8,
            "category_risk": 0
          },
          "factors_detected": [
            "questionable_merchant",
            "high_velocity"
          ],
          "reasoning": "Recurring subscription service with known billing issues. Multiple charges detected from same merchant. Moderate merchant reputation score.",
          "recommendation": "monitor_closely"
        },
        this.transaction_id == "TXN-89015" && this.customer_id == "CUST-1003" => {
          "transaction_id": "TXN-89015",
          "customer_id": "CUST-1003",
          "fraud_score": 12,
          "risk_level": "minimal",
          "score_breakdown": {
            "location_risk": 0,
            "merchant_risk": 0,
            "amount_risk": 5,
            "velocity_risk": 0,
            "category_risk": 0
          },
          "factors_detected": [
            "slightly_elevated_amount"
          ],
          "reasoning": "International hotel charge consistent with customer's frequent travel patterns. Amount within expected range for lodging category.",
          "recommendation": "approve"
        },
        _ => {
          "transaction_id": this.transaction_id,
          "customer_id": this.customer_id,
          "fraud_score": 50,
          "risk_level": "medium",
          "score_breakdown": {
            "location_risk": 0,
            "merchant_risk": 0,
            "amount_risk": 0,
            "velocity_risk": 0,
            "category_risk": 0
          },
          "factors_detected": [],
          "reasoning": "Insufficient data to calculate accurate fraud score for this transaction/customer combination.",
          "recommendation": "monitor_closely"
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Calculate fraud risk score based on transaction patterns and risk indicators. Use TXN-89012 through TXN-89015 with corresponding customer IDs for testing."
        properties:
          - name: transaction_id
            type: string
            description: "Transaction identifier to analyze (format TXN-XXXXX)"
            required: true
          - name: customer_id
            type: string
            description: "Customer identifier for historical analysis (format CUST-XXXX)"
            required: true

    This tool provides detailed fraud signals with severity levels. Each indicator includes a description that agents can use to explain findings to customers.

    label: get_risk_indicators
    mapping: |
      root = match {
        this.transaction_id == "TXN-89012" => {
          "transaction_id": "TXN-89012",
          "risk_indicators": [
            {
              "indicator": "international_transaction",
              "severity": "high",
              "description": "Transaction originated from Singapore, customer has no international transaction history"
            },
            {
              "indicator": "first_time_merchant",
              "severity": "medium",
              "description": "Customer has never transacted with this merchant before"
            },
            {
              "indicator": "unusual_category",
              "severity": "high",
              "description": "Jewelry purchase is outside customer's typical spending categories"
            },
            {
              "indicator": "high_amount",
              "severity": "high",
              "description": "Transaction amount is 14.5x customer's average transaction"
            },
            {
              "indicator": "merchant_flagged",
              "severity": "critical",
              "description": "Merchant has been flagged in fraud databases"
            }
          ],
          "total_indicators": 5,
          "critical_count": 1,
          "high_count": 3,
          "medium_count": 1,
          "overall_assessment": "high_fraud_probability"
        },
        this.transaction_id == "TXN-89013" => {
          "transaction_id": "TXN-89013",
          "risk_indicators": [
            {
              "indicator": "known_merchant",
              "severity": "none",
              "description": "Example Marketplace is a recognized and trusted merchant"
            }
          ],
          "total_indicators": 1,
          "critical_count": 0,
          "high_count": 0,
          "medium_count": 0,
          "overall_assessment": "low_fraud_probability"
        },
        this.transaction_id == "TXN-89014" => {
          "transaction_id": "TXN-89014",
          "risk_indicators": [
            {
              "indicator": "recurring_billing",
              "severity": "low",
              "description": "Subscription service with recurring charges"
            },
            {
              "indicator": "merchant_billing_issues",
              "severity": "medium",
              "description": "Merchant has known history of duplicate billing complaints"
            },
            {
              "indicator": "duplicate_charge_pattern",
              "severity": "medium",
              "description": "Multiple charges detected from same merchant in short timeframe"
            }
          ],
          "total_indicators": 3,
          "critical_count": 0,
          "high_count": 0,
          "medium_count": 2,
          "low_count": 1,
          "none_count": 0,
          "overall_assessment": "medium_fraud_probability"
        },
        this.transaction_id == "TXN-89015" => {
          "transaction_id": "TXN-89015",
          "risk_indicators": [
            {
              "indicator": "international_transaction",
              "severity": "low",
              "description": "Transaction in France matches customer's travel history"
            },
            {
              "indicator": "travel_category",
              "severity": "none",
              "description": "Hotel charge is consistent with customer's frequent travel patterns"
            },
            {
              "indicator": "timing_matches_travel",
              "severity": "none",
              "description": "Transaction date aligns with customer's Paris trip"
            }
          ],
          "total_indicators": 3,
          "critical_count": 0,
          "high_count": 0,
          "medium_count": 0,
          "low_count": 1,
          "none_count": 2,
          "overall_assessment": "low_fraud_probability"
        },
        _ => {
          "transaction_id": this.transaction_id,
          "risk_indicators": [],
          "total_indicators": 0,
          "critical_count": 0,
          "high_count": 0,
          "medium_count": 0,
          "low_count": 0,
          "none_count": 0,
          "overall_assessment": "insufficient_data"
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Retrieve fraud risk indicators for a transaction including severity levels and overall assessment. Use TXN-89012 through TXN-89015 for testing."
        properties:
          - name: transaction_id
            type: string
            description: "Transaction identifier to analyze (format TXN-XXXXX)"
            required: true
  4. Click Create MCP Server.

Wait for the server status to show Running.

Merchant tools

Merchant tools verify business legitimacy and analyze merchant categories.

  1. Click Create MCP Server.

  2. Configure the server:

    • Name: merchant-tools

    • Description: Merchant verification and category analysis

    • Resource Tier: XSmall

  3. Add the following tools. For each tool, select Processor from the component type dropdown, then click Lint to validate:

    • verify_merchant

    • get_merchant_category

    This tool returns reputation scores, fraud report counts, business verification status, and red flags. Notice how it includes common issues for legitimate merchants (like subscription billing problems) to help agents distinguish between fraud and merchant operational issues.

    label: verify_merchant
    mapping: |
      root = match {
        this.merchant_name == "LUXURY WATCHES INT" => {
          "merchant_name": "LUXURY WATCHES INT",
          "merchant_id": "MER-99912",
          "reputation_score": 12,
          "reputation_level": "high_risk",
          "verification_status": "unverified",
          "fraud_reports": {
            "total_reports": 247,
            "recent_reports_30d": 42,
            "confirmed_fraud_cases": 89
          },
          "business_details": {
            "country": "Singapore",
            "years_in_operation": 1,
            "registration_verified": false
          },
          "red_flags": [
            "High volume of fraud reports",
            "Recently established business",
            "Unverified business registration",
            "Operates in high-risk category",
            "Pattern of chargebacks"
          ],
          "recommendation": "block_merchant"
        },
        this.merchant_name == "EXAMPLE MKTPLACE" => {
          "merchant_name": "EXAMPLE MKTPLACE",
          "merchant_id": "MER-00001",
          "reputation_score": 98,
          "reputation_level": "excellent",
          "verification_status": "verified",
          "fraud_reports": {
            "total_reports": 1203,
            "recent_reports_30d": 15,
            "confirmed_fraud_cases": 0
          },
          "business_details": {
            "country": "USA",
            "years_in_operation": 20,
            "registration_verified": true,
            "parent_company": "Example Organization"
          },
          "red_flags": [],
          "recommendation": "trusted_merchant"
        },
        this.merchant_name == "EXAMPLE STREAMING" => {
          "merchant_name": "EXAMPLE STREAMING",
          "merchant_id": "MER-45678",
          "reputation_score": 65,
          "reputation_level": "moderate",
          "verification_status": "verified",
          "fraud_reports": {
            "total_reports": 892,
            "recent_reports_30d": 67,
            "confirmed_fraud_cases": 12
          },
          "business_details": {
            "country": "USA",
            "years_in_operation": 5,
            "registration_verified": true
          },
          "red_flags": [
            "Known billing system issues",
            "Frequent duplicate charge complaints",
            "Difficult cancellation process"
          ],
          "common_issues": [
            "Duplicate subscriptions",
            "Failed cancellation processing",
            "Unclear billing descriptors"
          ],
          "recommendation": "verify_subscription_details"
        },
        this.merchant_name == "HOTEL PARIS" => {
          "merchant_name": "HOTEL PARIS",
          "merchant_id": "MER-78234",
          "reputation_score": 88,
          "reputation_level": "trusted",
          "verification_status": "verified",
          "fraud_reports": {
            "total_reports": 45,
            "recent_reports_30d": 2,
            "confirmed_fraud_cases": 0
          },
          "business_details": {
            "country": "France",
            "years_in_operation": 15,
            "registration_verified": true,
            "chain": "Independent Boutique Hotels"
          },
          "red_flags": [],
          "pricing": {
            "average_room_rate_eur": 280,
            "typical_range_eur": "220-350"
          },
          "recommendation": "legitimate_merchant"
        },
        _ => {
          "merchant_name": this.merchant_name,
          "reputation_score": 50,
          "reputation_level": "unknown",
          "verification_status": "not_found",
          "fraud_reports": {
            "total_reports": 0,
            "recent_reports_30d": 0,
            "confirmed_fraud_cases": 0
          },
          "business_details": {},
          "red_flags": [],
          "message": "Merchant not found in verification database",
          "recommendation": "manual_review_required"
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Verify merchant reputation and fraud history. Use LUXURY WATCHES INT (high risk), EXAMPLE MKTPLACE (trusted), EXAMPLE STREAMING (moderate), or HOTEL PARIS (trusted) for testing."
        properties:
          - name: merchant_name
            type: string
            description: "Merchant name as it appears on transaction"
            required: true

    This tool decodes MCC (Merchant Category Codes) and provides typical transaction ranges for each category. This helps identify mismatches (like a grocery store charging $2000).

    label: get_merchant_category
    mapping: |
      root = match {
        this.mcc == "5944" => {
          "mcc": "5944",
          "category": "Jewelry, Watch, Clock, and Silverware Stores",
          "high_level_category": "retail_luxury",
          "risk_profile": "high",
          "typical_transaction_range": {
            "min": 100,
            "max": 5000,
            "average": 850
          },
          "fraud_risk_notes": "High-value items, common fraud target, verify customer intent",
          "common_fraud_patterns": [
            "Stolen card purchases",
            "Account takeover",
            "Reshipping schemes"
          ]
        },
        this.mcc == "5942" => {
          "mcc": "5942",
          "category": "Book Stores",
          "high_level_category": "retail_general",
          "risk_profile": "low",
          "typical_transaction_range": {
            "min": 10,
            "max": 200,
            "average": 45
          },
          "fraud_risk_notes": "Low fraud risk, common online purchase category",
          "common_fraud_patterns": []
        },
        this.mcc == "4899" => {
          "mcc": "4899",
          "category": "Cable, Satellite, and Other Pay Television and Radio Services",
          "high_level_category": "subscription_services",
          "risk_profile": "medium",
          "typical_transaction_range": {
            "min": 9.99,
            "max": 99.99,
            "average": 29.99
          },
          "fraud_risk_notes": "Recurring billing, watch for duplicate charges and unauthorized subscriptions",
          "common_fraud_patterns": [
            "Duplicate subscriptions",
            "Unauthorized recurring charges",
            "Failed cancellation processing"
          ]
        },
        this.mcc == "7011" => {
          "mcc": "7011",
          "category": "Lodging - Hotels, Motels, Resorts",
          "high_level_category": "travel_hospitality",
          "risk_profile": "medium",
          "typical_transaction_range": {
            "min": 80,
            "max": 500,
            "average": 180
          },
          "fraud_risk_notes": "Verify travel patterns, check for location consistency",
          "common_fraud_patterns": [
            "Stolen card at booking sites",
            "Account takeover for rewards redemption"
          ]
        },
        _ => {
          "mcc": this.mcc,
          "category": "Unknown Category",
          "high_level_category": "unclassified",
          "risk_profile": "unknown",
          "typical_transaction_range": {
            "min": 0,
            "max": 0,
            "average": 0
          },
          "fraud_risk_notes": "MCC not recognized, manual review recommended",
          "common_fraud_patterns": []
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Retrieve merchant category information including fraud risk level and common patterns based on MCC code."
        properties:
          - name: mcc
            type: string
            description: "Merchant Category Code (5944 for jewelry, 5942 for books, 4899 for streaming, 7011 for hotels)"
            required: true
  4. Click Create MCP Server.

Wait for the server status to show Running.

Compliance tools

Compliance tools handle audit logging and regulatory requirements.

  1. Click Create MCP Server.

  2. Configure the server:

    • Name: compliance-tools

    • Description: Audit logging and regulatory compliance

    • Resource Tier: XSmall

  3. Add the following tools. For each tool, select Processor from the component type dropdown, then click Lint to validate:

    • log_audit_event

    • check_regulatory_requirements

    This tool creates audit records for every investigation. In production, this would write to an immutable audit log. For this tutorial, it returns a confirmation with the audit ID.

    label: log_audit_event
    processors:
      - mapping: |
          root = {
            "audit_id": uuid_v4(),
            "timestamp": now(),
            "event_type": "dispute_investigation",
            "transaction_id": this.transaction_id,
            "customer_id": this.customer_id,
            "agent_decision": this.decision,
            "risk_score": this.risk_score,
            "evidence_reviewed": this.evidence,
            "outcome": this.outcome,
            "escalated": this.escalated,
            "compliance_notes": this.notes,
            "logged_by": "dispute-resolution-agent",
            "status": "recorded"
          }
    
      - log:
          level: INFO
          message: "Compliance audit event: ${!json()}"
    
    meta:
      mcp:
        enabled: true
        description: "Log compliance audit events for dispute resolution. Records customer ID, transaction details, decision, and notes."
        properties:
          - name: customer_id
            type: string
            description: "Customer identifier (format CUST-XXXX)"
            required: true
          - name: transaction_id
            type: string
            description: "Transaction identifier (format TXN-XXXXX)"
            required: true
          - name: decision
            type: string
            description: "Dispute resolution decision (approve_refund, deny_claim, etc.)"
            required: true
          - name: risk_score
            type: number
            description: "Calculated fraud risk score (0-100)"
            required: true
          - name: evidence
            type: object
            description: "Evidence reviewed during investigation"
            required: true
          - name: outcome
            type: string
            description: "Final outcome of the dispute (approved, denied, escalated, pending)"
            required: true
          - name: escalated
            type: boolean
            description: "Whether case was escalated for manual review"
            required: false
          - name: notes
            type: string
            description: "Additional compliance notes"
            required: false

    This tool returns applicable regulations, customer rights, bank obligations, and required documentation for different dispute types. This ensures agents follow proper procedures for Regulation E, Fair Credit Billing Act, and card network rules.

    label: check_regulatory_requirements
    mapping: |
      root = match {
        this.dispute_type == "fraud" => {
          "dispute_type": "fraud",
          "regulations_applicable": [
            "Regulation E (Electronic Fund Transfer Act)",
            "Fair Credit Billing Act",
            "Card Network Rules (Visa/Mastercard)"
          ],
          "customer_rights": {
            "liability_limit": 50.00,
            "zero_liability_if_reported_promptly": true,
            "notification_deadline_days": 60
          },
          "bank_obligations": {
            "provisional_credit_required": true,
            "provisional_credit_deadline_days": 10,
            "investigation_deadline_days": 90,
            "customer_notification_required": true
          },
          "documentation_required": [
            "Customer dispute affidavit",
            "Transaction details",
            "Customer communication log",
            "Investigation findings"
          ],
          "timeline": {
            "acknowledge_dispute_hours": 24,
            "provisional_credit_days": 10,
            "final_decision_days": 90
          }
        },
        this.dispute_type == "billing_error" => {
          "dispute_type": "billing_error",
          "regulations_applicable": [
            "Fair Credit Billing Act",
            "Regulation Z (Truth in Lending)"
          ],
          "customer_rights": {
            "dispute_window_days": 60,
            "interest_suspension": true
          },
          "bank_obligations": {
            "acknowledge_dispute_days": 30,
            "investigation_deadline_days": 90,
            "correction_required_if_error_found": true
          },
          "documentation_required": [
            "Billing statement",
            "Customer dispute letter",
            "Merchant communication (if any)",
            "Investigation results"
          ],
          "timeline": {
            "acknowledge_dispute_days": 30,
            "resolution_days": 90
          }
        },
        this.dispute_type == "service_not_received" => {
          "dispute_type": "service_not_received",
          "regulations_applicable": [
            "Fair Credit Billing Act",
            "Card Network Chargeback Rules"
          ],
          "customer_rights": {
            "chargeback_eligibility": true,
            "dispute_window_days": 120
          },
          "bank_obligations": {
            "verify_merchant_response": true,
            "chargeback_processing_days": 45
          },
          "documentation_required": [
            "Proof of non-delivery or service failure",
            "Merchant communication attempts",
            "Order/booking confirmation",
            "Merchant response (if obtained)"
          ],
          "timeline": {
            "merchant_response_wait_days": 15,
            "chargeback_filing_days": 120
          }
        },
        _ => {
          "dispute_type": "general",
          "regulations_applicable": [
            "Fair Credit Billing Act"
          ],
          "customer_rights": {
            "dispute_right": true,
            "dispute_window_days": 60
          },
          "bank_obligations": {
            "investigation_required": true,
            "customer_notification_required": true
          },
          "documentation_required": [
            "Customer dispute statement",
            "Transaction evidence"
          ],
          "timeline": {
            "standard_review_days": 30
          }
        }
      }
    
    meta:
      mcp:
        enabled: true
        description: "Check regulatory requirements for dispute resolution based on dispute type."
        properties:
          - name: dispute_type
            type: string
            description: "Type of dispute (fraud, billing_error, service_not_received)"
            required: true
  4. Click Create MCP Server.

Wait for the server status to show Running. You now have four MCP servers with nine total tools, organized by domain.

Create the root agent with subagents

The root agent orchestrates sub-agents and makes final recommendations. You’ll configure the root agent first, then add four specialized sub-agents within the same form.

Sub-agents inherit the LLM provider, model, resource tier, and max iterations from the root agent. This tutorial uses GPT-5 Mini and max iterations of 15 to optimize performance. Using slower models (GPT-5.2, Claude Sonnet 4.5) or high max iterations (50+) will cause sub-agents to execute slowly. Each sub-agent call could take 60-90 seconds instead of 10-15 seconds.

  1. Go to Agentic AI > AI Agents.

  2. Click Create Agent.

  3. Configure the root agent:

    • Name: dispute-resolution-agent

    • Description: Orchestrates transaction dispute investigations

    • Resource Tier: Large

    • AI Gateway: Select the gateway you configured

    • Provider: OpenAI

    • Model: GPT-5 Mini (fast, cost-effective for structured workflows)

    • Max Iterations: 15

  4. In the System Prompt field, enter:

    You are the root agent for a transaction dispute resolution system at ACME Bank. Your role is to orchestrate sub-agents and make final recommendations to customers about disputed transactions.
    
    ## Your Responsibilities
    
    - Route customer queries to appropriate sub-agents
    - Aggregate results from multiple sub-agents
    - Make evidence-based recommendations
    - Communicate clearly with customers
    - Escalate complex cases to human agents
    
    ## Available Sub-Agents
    
    You have access to four specialized sub-agents via A2A protocol:
    
    1. **account-agent**: Retrieves customer account data and transaction history
    2. **fraud-agent**: Analyzes fraud risk and calculates risk scores
    3. **merchant-agent**: Verifies merchant legitimacy and reputation
    4. **compliance-agent**: Logs audit events and checks regulatory requirements
    
    ## Decision Framework
    
    When investigating a dispute, follow this process:
    
    1. Start with account-agent to get customer and transaction details
    2. Route to fraud-agent if fraud is suspected
    3. Route to merchant-agent to verify merchant legitimacy
    4. Route to compliance-agent to log the investigation and check requirements
    5. Aggregate all evidence and make recommendation
    
    ## Risk-Based Recommendations
    
    Based on aggregated evidence, take these actions:
    
    - **Fraud score 80-100 + high merchant risk**: Block the transaction immediately, block the card, issue new card
    - **Fraud score 60-79**: Hold for specialist review, temporary card block
    - **Fraud score 40-59**: Ask customer to verify with merchant first before taking action
    - **Fraud score 0-39**: Likely legitimate transaction, help customer recall the purchase
    
    ## Escalation Criteria
    
    Escalate to human agent when:
    
    - Fraud score is medium (40-70) and evidence is conflicting
    - Customer disputes the recommendation strongly
    - Regulatory requirements exceed available tools
    - Subscription or recurring billing issues require merchant intervention
    
    ## Compliance Constraints
    
    Never:
    
    - Expose full credit card numbers or SSNs (use masked versions only)
    - Make guarantees about dispute outcomes (use "likely" or "recommend")
    - Process disputes without logging to compliance-agent
    - Reveal internal fraud detection logic or merchant scoring details to customers
    - Make decisions without sub-agent evidence
    - Ask customers for screenshots or additional proof (you have the transaction records)
    
    ## Customer Communication Style
    
    **Clear, bank-appropriate language:**
    - Use "I've reviewed your account" not "I called the account-agent"
    - Use "this charge doesn't match your typical spending" not "fraud score is 95/100"
    - Use "We're blocking this card" not "I recommend you freeze it"
    - Don't reveal merchant reputation scores or fraud report counts
    
    **Proactive protection:**
    For likely fraud (score 80+):
    - Block the card immediately: "We're blocking your card ending in [XXXX] right now to prevent additional fraudulent charges"
    - Issue replacement: "We'll send you a replacement card with a new number"
    - Process the claim: "You'll see the credit for this charge within 10 business days"
    
    For uncertain cases (score 40-79):
    - Temporary block: "I'm placing a temporary hold on this card while we investigate"
    - Escalate: "A specialist will contact you within 24 hours"
    
    **Concise responses:**
    Keep responses to 3-4 short paragraphs maximum. Customers want action, not detailed analysis.
    
    ## Example Investigation Flow
    
    Customer: "I see a $1,847.99 charge from 'LUXURY WATCHES INT' in Singapore on transaction TXN-89012. This is fraud. My customer ID is CUST-1001."
    
    **Your response to customer:**
    
    "I've reviewed your account and this transaction. This charge doesn't match your typical spending pattern, and you haven't made international purchases in the past 90 days.
    
    Here's what I'm doing:
    - Blocking your card ending in 4532 right now to prevent any additional unauthorized charges
    - Approving your dispute for the full $1,847.99 - you'll see the credit within 10 business days
    - Sending you a replacement card with a new number within 5-7 business days
    
    Your dispute has been logged and meets the requirements under Regulation E for unauthorized electronic fund transfers.
    
    Is there anything else I can help you with today?"
    
    **What you actually did behind the scenes:**
    1. Called account-agent → confirmed US-based customer, no international history
    2. Called fraud-agent → received score 95/100 (critical risk)
    3. Called merchant-agent → confirmed high fraud indicators
    4. Called compliance-agent → logged under Regulation E
    5. Made decision: transaction is fraudulent, block card immediately
    
    (Don't share the scores or technical details with the customer)
    
    **Note:** When talking to customers, use natural banking language like "approving your dispute." But for programmatic JSON responses, "recommendation" describes the TRANSACTION status, not the dispute claim status.
    
    ## Programmatic Invocations
    
    When invoked from a pipeline or automated system (you'll receive transaction data without conversational context), respond with ONLY valid JSON. No explanatory text, no markdown formatting, no commentary before or after - just the JSON object.
    
    Required JSON format:
    {
      "recommendation": "block_and_investigate" | "hold_for_review" | "approve",
      "fraud_score": <number 0-100>,
      "confidence": "high" | "medium" | "low",
      "reasoning": "<brief explanation>"
    }
    
    **Recommendation field definitions:**
    - **"block_and_investigate"**: Transaction is fraudulent. Block the card immediately and investigate.
    - **"hold_for_review"**: Unclear if fraudulent. Place temporary hold and escalate to human specialist.
    - **"approve"**: Transaction is legitimate. Customer likely forgot about it or needs clarification.
    
    **Mapping from conversational actions:**
    - If you would block the card → use "block_and_investigate"
    - If you would escalate to specialist → use "hold_for_review"
    - If transaction seems legitimate → use "approve"
    
    The pipeline will parse this JSON to make automated decisions. Any non-JSON response will cause processing failures.
  5. Skip the MCP Tools section (the root agent uses A2A protocol to call sub-agents, not direct tools).

  6. In the Subagents section, click + Add Subagent.

Add account agent subagent

The account agent retrieves customer account and transaction data.

  1. Configure the subagent:

    • Name: account-agent

    • Description: Retrieves customer account and transaction data

  2. In the subagent’s System Prompt field, enter:

    You are the account agent for ACME Bank's dispute resolution system. You specialize in retrieving customer account information and transaction data.
    
    ## Your Responsibilities
    
    - Look up customer account details with PII masking
    - Retrieve specific transaction information
    - Provide transaction pattern analysis
    - Return only data available from your tools
    
    ## Available Tools
    
    1. **get_customer_account**: Returns account data with masked PII
       - Input: customer_id
       - Returns: Name, masked email, card last 4, account type, location
    
    2. **get_transaction_details**: Returns detailed transaction information
       - Input: transaction_id
       - Returns: Amount, merchant, date, location, card used
    
    3. **get_transaction_history**: Returns spending pattern analysis
       - Input: customer_id
       - Returns: Aggregated spending patterns, categories, locations
    
    ## PII Protection Rules
    
    Always return masked data:
    - Email: First letter + **** + @domain (for example, "s****@example.com")
    - Phone: ***-***-XXXX (last 4 digits only)
    - Card: Last 4 digits only
    - Never return: Full card numbers, SSNs, full account numbers
    
    ## Response Format
    
    Structure responses clearly:
    
    "I found the following account information:
    - Customer: [Name]
    - Account Type: [Type]
    - Card ending in: [Last 4]
    - Primary Location: [City, State, Country]
    
    Transaction details:
    - Amount: $[Amount]
    - Merchant: [Merchant Name]
    - Date: [Date]
    - Location: [Transaction Location]"
    
    ## Error Handling
    
    If data not found:
    - "I couldn't find an account for customer ID [ID]"
    - "No transaction found with ID [ID]"
    - Never guess or make up information
    
    ## What You Don't Do
    
    - Don't calculate fraud scores (that's fraud-agent's job)
    - Don't verify merchants (that's merchant-agent's job)
    - Don't make recommendations about disputes
    - Don't log audit events (that's compliance-agent's job)
    
    Your job is data retrieval only. Provide accurate, masked data and let the root agent make decisions.
  3. In the subagent’s MCP Tools section, select account-tools.

Add fraud agent subagent

The fraud agent calculates fraud risk scores and identifies fraud indicators.

  1. Click + Add Subagent again.

  2. Configure the subagent:

    • Name: fraud-agent

    • Description: Calculates fraud risk scores and identifies fraud indicators

  3. In the subagent’s System Prompt field, enter:

    You are the fraud detection agent for ACME Bank's dispute resolution system. You specialize in analyzing transactions for fraud indicators and calculating risk scores.
    
    ## Your Responsibilities
    
    - Calculate fraud risk scores (0-100 scale)
    - Identify specific fraud indicators
    - Provide risk assessment reasoning
    - Return confidence levels with assessments
    
    ## Available Tools
    
    1. **calculate_fraud_score**: Multi-factor fraud scoring
       - Input: transaction_id, customer_id
       - Returns: Fraud score (0-100), risk level, breakdown by factor, recommendation
    
    2. **get_risk_indicators**: Detailed fraud signal detection
       - Input: transaction_id
       - Returns: Array of risk indicators with severity levels
    
    ## Risk Scoring Factors
    
    Consider these factors:
    
    1. **Location Risk** (0-30 points)
       - International vs. customer's country
       - City mismatch from customer's primary location
       - High-risk countries
    
    2. **Merchant Risk** (0-25 points)
       - Merchant reputation score
       - Fraud report history
       - Business verification status
    
    3. **Amount Risk** (0-25 points)
       - Deviation from customer's average
       - Unusually large for merchant category
       - Round numbers (potential testing)
    
    4. **Velocity Risk** (0-10 points)
       - Multiple transactions in short timeframe
       - Rapid succession of purchases
       - Geographic impossibility
    
    5. **Category Risk** (0-10 points)
       - Outside customer's typical categories
       - High-risk MCC codes
       - Mismatch with spending patterns
    ## Risk Levels
    
    - **Critical (80-100)**: Almost certainly fraud, immediate action needed
    - **High (60-79)**: Strong fraud indicators, hold for review
    - **Medium (40-59)**: Some concerning factors, customer verification recommended
    - **Low (20-39)**: Minor flags, likely legitimate
    - **Minimal (0-19)**: No significant fraud indicators
    
    ## Response Format
    
    Structure your analysis:
    
    "Fraud Risk Analysis:
    
    Fraud Score: [Score]/100 - [Risk Level]
    
    Risk Breakdown:
    - Location Risk: [Score] - [Explanation]
    - Merchant Risk: [Score] - [Explanation]
    - Amount Risk: [Score] - [Explanation]
    - Velocity Risk: [Score] - [Explanation]
    - Category Risk: [Score] - [Explanation]
    
    Key Indicators:
    - [Indicator 1]
    - [Indicator 2]
    - [Indicator 3]
    
    Recommendation: [block_and_investigate | hold_for_review | monitor_closely | approve]"
    
    ## What You Don't Do
    
    - Don't retrieve account or transaction data (use what's provided)
    - Don't verify merchants (that's merchant-agent's job)
    - Don't make final dispute decisions (provide recommendation only)
    - Don't log audit events
    
    Your job is fraud analysis only. Provide objective risk assessment based on available data.
  4. In the subagent’s MCP Tools section, select fraud-tools.

Add merchant agent subagent

The merchant agent verifies merchant legitimacy and reputation.

  1. Click + Add Subagent again.

  2. Configure the subagent:

    • Name: merchant-agent

    • Description: Verifies merchant legitimacy and reputation

  3. In the subagent’s System Prompt field, enter:

    You are the merchant verification agent for ACME Bank's dispute resolution system. You specialize in verifying merchant legitimacy and reputation.
    
    ## Your Responsibilities
    
    - Verify merchant reputation and legitimacy
    - Look up merchant category codes (MCC)
    - Identify known fraud patterns for merchant categories
    - Provide merchant-specific insights
    
    ## Available Tools
    
    1. **verify_merchant**: Merchant reputation lookup
       - Input: merchant_name
       - Returns: Reputation score, fraud reports, business verification, red flags
    
    2. **get_merchant_category**: MCC code analysis
       - Input: mcc (4-digit code)
       - Returns: Category details, typical transaction ranges, fraud risk profile
    
    ## Reputation Scoring
    
    Interpret reputation scores:
    
    - **90-100**: Excellent, trusted merchant
    - **70-89**: Good, established business
    - **50-69**: Moderate, some concerns
    - **30-49**: Poor, significant red flags
    - **0-29**: High risk, strong fraud indicators
    
    ## Red Flags to Report
    
    Watch for:
    - High volume of fraud reports
    - Recently established businesses in high-risk categories
    - Unverified business registration
    - Pattern of chargebacks
    - Operates in high-risk jurisdictions
    - Billing descriptor mismatches
    
    ## Common Merchant Issues
    
    Be aware of legitimate merchant problems:
    
    - **Subscription services**: Known for duplicate billing, difficult cancellation
    - **International hotels**: Currency conversion confusion, incidental charges
    - **Online marketplaces**: Third-party sellers, billing descriptor confusion
    - **Travel booking**: Pre-authorization holds, cancellation fee disputes
    
    ## Response Format
    
    Structure your verification:
    
    "Merchant Verification Results:
    
    Merchant: [Name]
    Reputation Score: [Score]/100 - [Level]
    Verification Status: [Verified | Unverified | Unknown]
    
    Business Details:
    - Country: [Country]
    - Years in Operation: [Years]
    - Registration: [Verified/Unverified]
    
    Fraud Reports:
    - Total Reports: [Count]
    - Recent (30 days): [Count]
    - Confirmed Fraud Cases: [Count]
    
    Category Analysis (MCC [Code]):
    - Category: [Category Name]
    - Risk Profile: [High/Medium/Low]
    - Typical Transaction Range: $[Min]-$[Max]
    
    Red Flags:
    - [Flag 1]
    - [Flag 2]
    
    Recommendation: [trusted_merchant | verify_subscription_details | manual_review_required | block_merchant]"
    
    ## What You Don't Do
    
    - Don't calculate fraud scores (that's fraud-agent's job)
    - Don't retrieve transaction data (that's account-agent's job)
    - Don't make final dispute decisions
    - Don't log audit events
    
    Your job is merchant verification only. Provide objective assessment of merchant legitimacy.
  4. In the subagent’s MCP Tools section, select merchant-tools.

Add compliance agent subagent

The compliance agent handles audit logging and regulatory requirements.

  1. Click + Add Subagent again.

  2. Configure the subagent:

    • Name: compliance-agent

    • Description: Handles audit logging and regulatory requirements

  3. In the subagent’s System Prompt field, enter:

    You are the compliance agent for ACME Bank's dispute resolution system. You specialize in regulatory requirements and audit logging.
    
    ## Your Responsibilities
    
    - Log all dispute investigation actions for audit trail
    - Check regulatory requirements for dispute types
    - Verify compliance with banking regulations
    - Provide timeline and documentation requirements
    
    ## Available Tools
    
    1. **log_audit_event**: Log investigation actions
       - Input: Transaction ID, customer ID, decision, evidence, outcome
       - Returns: Audit record confirmation
    
    2. **check_regulatory_requirements**: Look up compliance rules
       - Input: dispute_type (fraud, billing_error, service_not_received)
       - Returns: Regulations, timelines, documentation requirements
    
    ## Regulatory Frameworks
    
    You work with these regulations:
    
    1. **Regulation E (Electronic Fund Transfer Act)**
       - Applies to: Fraud disputes, unauthorized transactions
       - Customer liability: $50 if reported within 2 days, $500 if reported within 60 days
       - Bank must provide provisional credit within 10 business days
       - Investigation deadline: 90 days
    
    2. **Fair Credit Billing Act**
       - Applies to: Billing errors, disputes
       - Customer must dispute within 60 days of statement
       - Bank must acknowledge within 30 days
       - Resolution deadline: 90 days
    
    3. **Card Network Rules (Visa/Mastercard)**
       - Chargeback rights and timelines
       - Merchant response requirements
       - Evidence requirements
    
    ## Documentation Requirements
    
    For each dispute type, log:
    
    **Fraud Disputes:**
    - Customer dispute affidavit
    - Transaction details
    - Fraud indicators identified
    - Decision and reasoning
    - Customer notification
    
    **Billing Errors:**
    - Billing statement
    - Customer dispute letter
    - Merchant communication attempts
    - Resolution details
    
    **Service Not Received:**
    - Proof of non-delivery
    - Merchant communication attempts
    - Order/booking confirmation
    - Resolution outcome
    
    ## Timeline Tracking
    
    Monitor key deadlines:
    
    - Acknowledge dispute: 24-30 days (varies by type)
    - Provisional credit: 10 business days (fraud)
    - Final decision: 90 days (most disputes)
    - Chargeback filing: 120 days (service issues)
    
    ## Response Format
    
    For regulatory checks:
    
    "Compliance Requirements:
    
    Dispute Type: [Type]
    Applicable Regulations:
    - [Regulation 1]
    - [Regulation 2]
    
    Customer Rights:
    - Liability Limit: $[Amount]
    - Notification Deadline: [Days] days
    
    Bank Obligations:
    - Provisional Credit: [Required/Not Required]
    - Investigation Deadline: [Days] days
    - Customer Notification: [Required/Not Required]
    
    Documentation Required:
    - [Document 1]
    - [Document 2]
    - [Document 3]
    
    Timeline:
    - Acknowledge: [Timeframe]
    - Decision: [Timeframe]"
    
    For audit logging:
    
    "Audit Event Logged:
    
    Audit ID: [UUID]
    Timestamp: [ISO 8601]
    Investigation Details: [Summary]
    Decision: [Decision]
    Evidence: [Evidence Sources]
    Status: Recorded"
    
    ## What You Don't Do
    
    - Don't retrieve transaction or account data
    - Don't calculate fraud scores
    - Don't verify merchants
    - Don't make dispute recommendations
    
    Your job is compliance and audit only. Ensure all investigations are properly documented and regulatory requirements are met.
  4. In the subagent’s MCP Tools section, select compliance-tools.

  5. Click Create Agent to create the root agent with all four subagents.

Wait for the agent status to show Running.

Test investigation scenarios

Test the multi-agent system with realistic dispute scenarios. Each scenario demonstrates different patterns: clear fraud, legitimate transactions, escalation cases, and edge cases.

  1. Go to Agentic AI > AI Agents.

  2. Click on dispute-resolution-agent.

  3. Open the Inspector tab.

Clear fraud case

Test how the system handles obvious fraud.

Enter this query:

I see a $1,847.99 charge from 'LUXURY WATCHES INT' in Singapore on transaction TXN-89012. I've never been to Singapore and don't buy watches. My customer ID is CUST-1001. This is fraud.

Watch the conversation panel as the investigation progresses. You’ll see the root agent call each sub-agent in sequence. After all sub-agents complete (30-90 seconds), the agent sends its final response to the chat.

The final response should clearly state the transaction is fraudulent, summarize findings from each sub-agent, and provide a list of actions the agent is going to take.

This flow demonstrates multi-agent coordination for high-confidence fraud decisions with realistic banking communication.

Escalation required

Test how the system handles ambiguous cases requiring human review.

Click Clear context. Then enter:

I see three $29.99 charges from 'EXAMPLE STREAMING' last month, but I only subscribed once. My customer ID is CUST-1002 and one of the transactions is TXN-89014.

Watch the conversation panel as the agent investigates. After the sub-agent calls complete, the agent should send a response with a realistic escalation.

This demonstrates the escalation pattern when evidence is ambiguous and requires human review.

Monitor multi-agent execution

Inspector shows real-time progress in the conversation panel, but Transcripts provides detailed post-execution analysis with timing, token usage, and full trace hierarchy.

  1. In the left navigation, click Transcripts.

  2. Select a recent transcript from your fraud case test.

In the trace hierarchy, you’ll see:

  • Root agent invocation (top-level span)

  • Multiple invoke_agent spans for each sub-agent call

  • Individual LLM calls within each agent

  • MCP tool invocations within sub-agents

In the summary panel, check:

  • Duration: Total investigation time (typically 5-15 seconds)

  • Token Usage: Cost tracking across all agents

  • LLM Calls: How many reasoning steps were needed

This visibility helps you:

  • Verify sub-agents are being called in the right order

  • Identify slow sub-agents that need optimization

  • Track costs per investigation for budgeting

For detailed trace structure, see Agent trace hierarchy.

Integrate with streaming pipeline

Process disputes automatically from transaction streams. When transactions meet certain risk thresholds, the pipeline invokes the dispute agent for immediate investigation.

Create a secret for the agent card URL

The pipeline needs the agent card URL to invoke the dispute resolution agent.

  1. Go to Agentic AI > AI Agents.

  2. Click on dispute-resolution-agent.

  3. Open the A2A tab.

  4. Copy the agent URL displayed at the top.

  5. Go to Connect > Secrets.

  6. Click Create Secret.

  7. Create the secret:

    • Name: DISPUTE_AGENT_CARD_URL

    • Value: Paste the agent URL and append /.well-known/agent-card.json to the end

      For example, if the agent URL is:

      https://abc123.ai-agents.def456.cloud.redpanda.com

      The secret value should be:

      https://abc123.ai-agents.def456.cloud.redpanda.com/.well-known/agent-card.json
  8. Click Create Secret.

Create topics for transaction data

Create the topics the pipeline will use for input and output.

  1. Go to Topics in the Redpanda Cloud Console.

  2. Click Create Topic.

  3. Create the input topic:

    • Name: bank.transactions

    • Partitions: 3

    • Replication factor: 3

  4. Click Create Topic again.

  5. Create the output topic:

    • Name: bank.dispute_results

    • Partitions: 3

    • Replication factor: 3

Create a SASL user for topic access

The pipeline needs SASL credentials to read from and write to Redpanda topics.

  1. Go to Security > Users in the Redpanda Cloud Console.

  2. Click Create User.

  3. Configure the user:

    • Username: dispute-pipeline-user

    • Password: Generate a secure password

    • Mechanism: SCRAM-SHA-256

  4. Save the username and password. You’ll need them for the pipeline secrets.

  5. Click Create.

  6. Click Create ACLs to grant permissions.

  7. Click the Clusters tab for cluster permissions and select Allow all.

  8. Click Add rule to add another ACL.

  9. Click the Topics tab for topic permissions:

    • Principal: dispute-pipeline-user

    • Host: Allow all hosts (*)

    • Resource Type: Topic

    • Selector: Topic names starting with bank.

    • Operations: Allow all

  10. Click Add rule to add another ACL.

  11. Click the Consumer groups tab for consumer group permissions and select Allow all.

  12. Click Create.

Create secrets for SASL authentication

The pipeline needs SASL credentials stored as secrets to authenticate with Redpanda topics.

  1. Go to Connect > Secrets in the Redpanda Cloud Console (if not already there).

  2. Click Create Secret.

  3. Create two secrets with these values:

    • Name: DISPUTE_PIPELINE_USERNAME, Value: dispute-pipeline-user

    • Name: DISPUTE_PIPELINE_PASSWORD, Value: The password you created for dispute-pipeline-user

Create the pipeline

  1. Go to Connect in the Redpanda Cloud Console.

  2. Click Create a pipeline.

  3. In the numbered steps, click 4 Add permissions.

  4. Select Service Account.

    The Service Account is required for the a2a_message processor to authenticate with and invoke the dispute resolution agent. Without this permission, the pipeline will fail when attempting to call the agent.

  5. Click Next.

  6. Name the pipeline dispute-pipeline.

  7. Paste this configuration and click Create Pipeline:

    # Event-driven transaction dispute processing pipeline
    # Automatically flags high-risk transactions and routes them to dispute agent
    
    input:
      kafka:
        addresses: ["${REDPANDA_BROKERS}"]
        topics: ["bank.transactions"]
        consumer_group: dispute-processor
        tls:
          enabled: true
        sasl:
          mechanism: SCRAM-SHA-256
          user: "${secrets.DISPUTE_PIPELINE_USERNAME}"
          password: "${secrets.DISPUTE_PIPELINE_PASSWORD}"
    
    pipeline:
      processors:
        # Filter for high-value or suspicious transactions
        - branch:
            request_map: |
              # Only process transactions above $500 or flagged by upstream systems
              root = if this.amount > 500 || this.preliminary_flag == true {
                this
              } else {
                deleted()
              }
    
            processors:
              # Calculate preliminary risk score based on transaction attributes
              - mapping: |
                  # Preserve original transaction
                  root = this
    
                  # Location risk: international transactions get higher score
                  let location_risk = if this.merchant.country != this.card.billing_country { 40 } else { 0 }
    
                  # Amount risk: large amounts relative to account averages
                  let amount_risk = if this.amount > 1000 { 30 } else if this.amount > 500 { 15 } else { 0 }
    
                  # Velocity risk: check for multiple recent transactions
                  let velocity_risk = if this.recent_transaction_count > 5 { 20 } else { 0 }
    
                  # Category risk: luxury goods and high-risk categories
                  let category_risk = match this.merchant.mcc {
                    "5944" => 20,  # Jewelry
                    "5094" => 25,  # Precious stones
                    _ => 0
                  }
    
                  # Calculate total score
                  let total_score = $location_risk + $amount_risk + $velocity_risk + $category_risk
    
                  root.preliminary_risk_score = $total_score
                  root.risk_level = if $total_score > 70 {
                    "high"
                  } else if $total_score > 40 {
                    "medium"
                  } else {
                    "low"
                  }
    
              # Route high and medium risk transactions to dispute agent for investigation
              - branch:
                  request_map: |
                    # Only send to agent if risk is medium or higher
                    root = if this.preliminary_risk_score >= 40 { this } else { deleted() }
    
                  processors:
                    # Invoke dispute resolution agent via A2A protocol
                    - a2a_message:
                        agent_card_url: "${secrets.DISPUTE_AGENT_CARD_URL}"
                        prompt: |
                          Investigate this potentially fraudulent transaction and respond with ONLY a JSON object (no additional text):
    
                          Transaction ID: ${! this.transaction_id }
                          Customer ID: ${! this.customer_id }
                          Amount: $${! this.amount } ${! this.currency }
                          Merchant: ${! this.merchant.name }
                          Location: ${! this.merchant.city }, ${! this.merchant.country }
                          Date: ${! this.transaction_date }
                          Preliminary Risk Score: ${! this.preliminary_risk_score }/100
                          Risk Level: ${! this.risk_level }
    
                          Return ONLY this JSON format with no other text:
                          {
                            "recommendation": "block_and_investigate" | "hold_for_review" | "approve",
                            "fraud_score": <number 0-100>,
                            "confidence": "high" | "medium" | "low",
                            "reasoning": "<brief explanation>"
                          }
    
                  # Map agent response back to transaction record
                  result_map: |
                    # By default, result_map preserves the original message that entered the branch
                    # Just add the agent investigation field
                    root.agent_investigation = if content().string().parse_json().catch(null) != null {
                      content().string().parse_json()
                    } else {
                      {
                        "recommendation": "manual_review_required",
                        "fraud_score": 50,
                        "confidence": "low",
                        "reasoning": "Agent returned unparseable response: " + content().string().slice(0, 100)
                      }
                    }
    
            # Merge risk scoring and agent results back to original transaction
            result_map: |
              root = content()
    
        # Enrich with final decision and tracing metadata
        - mapping: |
            # Preserve original transaction and all computed fields
            root = this
    
            # Only set final_decision and alert_level if agent investigation occurred
            root.final_decision = if this.agent_investigation.exists("recommendation") {
              match {
                this.agent_investigation.recommendation == "block_and_investigate" => "blocked",
                this.agent_investigation.recommendation == "hold_for_review" => "pending_review",
                this.agent_investigation.recommendation == "approve" => "approved",
                _ => "manual_review_required"
              }
            } else {
              "low_risk_no_investigation"
            }
    
            root.alert_level = if this.agent_investigation.exists("fraud_score") {
              match {
                this.agent_investigation.fraud_score >= 80 => "critical",
                this.agent_investigation.fraud_score >= 60 => "high",
                this.agent_investigation.fraud_score >= 40 => "medium",
                _ => "low"
              }
            } else {
              "low"
            }
    
            # Add execution metadata for tracing back to agent transcripts
            root.pipeline_metadata = {
              "processed_at": now().ts_format("2006-01-02T15:04:05.000Z"),
              "transaction_id": this.transaction_id,
              "customer_id": this.customer_id,
              "agent_invoked": this.agent_investigation.exists("fraud_score")
            }
    
    output:
      kafka:
        addresses: ["${REDPANDA_BROKERS}"]
        topic: bank.dispute_results
        key: "${! this.transaction_id }"
        tls:
          enabled: true
        sasl:
          mechanism: SCRAM-SHA-256
          user: "${secrets.DISPUTE_PIPELINE_USERNAME}"
          password: "${secrets.DISPUTE_PIPELINE_PASSWORD}"

This pipeline:

  • Consumes transactions from bank.transactions topic

  • Filters for high-value transactions (>$500) or pre-flagged transactions

  • Calculates preliminary risk score based on location, amount, velocity, and category

  • Routes transactions with risk score ≥40 to the dispute-resolution-agent via A2A

  • Outputs investigation results to bank.dispute_results topic

Test the pipeline

  1. Authenticate with your Redpanda Cloud cluster:

    rpk cloud login
  2. Create a test transaction that will trigger the agent investigation:

    echo '{
      "transaction_id": "TXN-89012",
      "customer_id": "CUST-1001",
      "amount": 1847.99,
      "currency": "USD",
      "merchant": {
        "name": "LUXURY WATCHES INT",
        "category": "jewelry",
        "country": "Singapore",
        "mcc": "5944",
        "city": "Singapore"
      },
      "card": {
        "last_four": "4532",
        "billing_country": "USA"
      },
      "transaction_date": "2026-01-21T10:00:00Z",
      "recent_transaction_count": 2
    }' | rpk topic produce bank.transactions

    This transaction will trigger agent investigation because:

    • International transaction (Singapore vs USA): +40 risk points

    • Amount is greater than $1000: +30 risk points

    • Jewelry category (MCC 5944): +20 risk points

    • Total preliminary risk score: 90 (well above the 40 threshold)

  3. Wait a minute for the pipeline to process the transaction. You can monitor the progress in Transcripts. While the agents investigate, a new transcript for dispute-resolution-agent will appear. Until the investigation completes, the transcript will show awaiting root status.

  4. Consume the results:

    rpk topic consume bank.dispute_results --offset end -n 1

    You’ll see the complete transaction with agent investigation results:

    {
        "agent_investigation": {
            "confidence": "high",
            "fraud_score": 91,
            "reasoning": "Transaction is an international purchase with no recent international activity, from a merchant with strong fraud indicators, and the amount is a large outlier for this account; immediate block and investigation recommended.",
            "recommendation": "block_and_investigate"
        },
        "alert_level": "critical",
        "amount": 1847.99,
        "card": {
            "billing_country": "USA",
            "last_four": "4532"
        },
        "currency": "USD",
        "customer_id": "CUST-1001",
        "final_decision": "blocked",
        "merchant": {
            "category": "jewelry",
            "city": "Singapore",
            "country": "Singapore",
            "mcc": "5944",
            "name": "LUXURY WATCHES INT"
        },
        "pipeline_metadata": {
            "agent_invoked": true,
            "customer_id": "CUST-1001",
            "processed_at": "2026-01-27T14:29:19.436Z",
            "transaction_id": "TXN-89012"
        },
        "preliminary_risk_score": 90,
        "recent_transaction_count": 2,
        "risk_level": "high",
        "transaction_date": "2026-01-21T10:00:00Z",
        "transaction_id": "TXN-89012"
    }

This output contains everything downstream systems need such as fraud monitoring, customer alerts, and audit logging.

The pipeline uses a two-stage filter:

  • Only processes transactions with amount > 500 or preliminary_flag == true

  • Only sends transactions to the agent if preliminary_risk_score >= 40

Transactions that pass the first filter but not the second (for example, a $600 domestic transaction with low risk) will appear in the output with:

  • final_decision: "low_risk_no_investigation"

  • alert_level: "low"

  • No agent_investigation field

Only transactions meeting the risk threshold invoke the dispute resolution agent.

Trace pipeline execution to agent transcripts

Use the pipeline metadata timestamp to find the corresponding agent execution in the Transcripts view.

  1. Note the processed_at timestamp from the pipeline output (for example: 2026-01-26T18:30:45.000Z).

  2. Go to Agentic AI > Transcripts.

  3. Find transcripts for dispute-resolution-agent that match your timestamp.

The search function does not search through prompt content or attribute values. Use the timestamp to narrow down the time window, then manually review transcripts from that period.

In the transcript details, you’ll see:

  • The full prompt sent to the agent (including transaction ID and details)

  • Each sub-agent invocation (account-agent, fraud-agent, merchant-agent, compliance-agent)

  • Token usage and execution time for the investigation

  • The complete JSON response returned to the pipeline

Troubleshoot

For comprehensive troubleshooting guidance, see Troubleshoot AI Agents.

Test with mock data

The mock tools in this tutorial use hardcoded customer and transaction IDs for testing:

  • Customer IDs: CUST-1001, CUST-1002, CUST-1003

  • Transaction IDs: TXN-89012, TXN-89013, TXN-89014, TXN-89015

Use these documented test IDs when testing in Inspector or the pipeline. The sub-agents' mock tools require valid IDs to return transaction details, account history, and fraud indicators. Using other IDs (like TXN-TEST-001 or CUST-9999) will cause the tools to return "not found" errors, and the root agent won’t be able to complete its investigation.

For production deployments, replace the mock tools with API calls to your account, fraud detection, merchant verification, and compliance systems.