{
  "format": "minctrl.process/v1",
  "graph": {
    "edges": [
      {
        "id": "e0___start____intake",
        "source": "__start__",
        "target": "intake"
      },
      {
        "id": "e1_intake__classify",
        "source": "intake",
        "target": "classify"
      },
      {
        "id": "e2_classify__disputeCheck",
        "source": "classify",
        "target": "disputeCheck"
      },
      {
        "id": "e3_disputeCheck__disputeInvestigation",
        "label": "active Reg E error dispute on file",
        "source": "disputeCheck",
        "target": "disputeInvestigation"
      },
      {
        "id": "e4_disputeCheck__scaCheck",
        "label": "no active dispute",
        "source": "disputeCheck",
        "target": "scaCheck"
      },
      {
        "id": "e5_disputeInvestigation__scaCheck",
        "label": "investigation complete; error determination made",
        "source": "disputeInvestigation",
        "target": "scaCheck"
      },
      {
        "id": "e6_scaCheck__failureType",
        "label": "non-EU customer or SCA exemption valid",
        "source": "scaCheck",
        "target": "failureType"
      },
      {
        "id": "e7_scaCheck__suspensionGate",
        "label": "EU customer; SCA required but failed and no exemption",
        "source": "scaCheck",
        "target": "suspensionGate"
      },
      {
        "id": "e8_failureType__refreshCard",
        "label": "expired card",
        "source": "failureType",
        "target": "refreshCard"
      },
      {
        "id": "e9_failureType__recoverySplit",
        "label": "soft decline / recoverable",
        "source": "failureType",
        "target": "recoverySplit"
      },
      {
        "id": "e10_failureType__suspensionGate",
        "label": "hard decline / unrecoverable",
        "source": "failureType",
        "target": "suspensionGate"
      },
      {
        "id": "e11_refreshCard__recoverySplit",
        "source": "refreshCard",
        "target": "recoverySplit"
      },
      {
        "id": "e12_recoverySplit__retry",
        "source": "recoverySplit",
        "target": "retry"
      },
      {
        "id": "e13_recoverySplit__dunning",
        "source": "recoverySplit",
        "target": "dunning"
      },
      {
        "id": "e14_retry__suspensionGate",
        "label": "retry spacing or attempt-limit exceeded",
        "source": "retry",
        "target": "suspensionGate"
      },
      {
        "id": "e15_retry__recovered",
        "source": "retry",
        "target": "recovered"
      },
      {
        "id": "e16_dunning__suspensionGate",
        "label": "cease-communication notice on file",
        "source": "dunning",
        "target": "suspensionGate"
      },
      {
        "id": "e17_dunning__recovered",
        "source": "dunning",
        "target": "recovered"
      },
      {
        "id": "e18_recovered__monitor",
        "label": "payment recovered",
        "source": "recovered",
        "target": "monitor"
      },
      {
        "id": "e19_recovered__concessionGate",
        "label": "recoverable with a concession",
        "source": "recovered",
        "target": "concessionGate"
      },
      {
        "id": "e20_recovered__suspensionGate",
        "label": "window exhausted / unrecoverable",
        "source": "recovered",
        "target": "suspensionGate"
      },
      {
        "id": "e21_concessionGate__execConcession",
        "source": "concessionGate",
        "target": "execConcession"
      },
      {
        "id": "e22_concessionGate__disputeInvestigation",
        "label": "open Reg E dispute detected at gate — halt concession",
        "source": "concessionGate",
        "target": "disputeInvestigation"
      },
      {
        "id": "e23_execConcession__retry",
        "source": "execConcession",
        "target": "retry"
      },
      {
        "id": "e24_suspensionGate__execSuspension",
        "source": "suspensionGate",
        "target": "execSuspension"
      },
      {
        "id": "e25_execSuspension__monitor",
        "source": "execSuspension",
        "target": "monitor"
      },
      {
        "id": "e26_monitor____end__",
        "source": "monitor",
        "target": "__end__"
      }
    ],
    "nodes": [
      {
        "agent": null,
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "__start__",
        "interrupt_before": false,
        "kind": "terminal",
        "label": "Start",
        "position": {
          "x": 0,
          "y": 0
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "terminal",
        "tools": []
      },
      {
        "agent": "intake",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "intake",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Ingest the failed-payment event and pull subscription, invoice, decline code, and prior dunning history",
        "position": {
          "x": 0,
          "y": 120
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "low",
        "tools": [
          "billing-platform",
          "core-ledger",
          "card-network:decline-code"
        ]
      },
      {
        "agent": "analyzer",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "classify",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Classify the failure (soft decline vs expired card vs hard decline) and score recovery likelihood against the recovery policy; map decline code to ISO 20022, Visa, and Mastercard response codes to distinguish transient issuer failures from card errors and hard stops",
        "position": {
          "x": 0,
          "y": 240
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "low",
        "tools": [
          "decline-code-taxonomy:iso20022-mapping",
          "decline-code-taxonomy:visa-mapping",
          "decline-code-taxonomy:mastercard-mapping",
          "recovery-policy",
          "ml-recovery-model"
        ]
      },
      {
        "agent": "intake",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "disputeCheck",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Query the dispute ledger: has the customer filed an error claim or dispute for this recurring payment within 60 days of statement date (Reg E § 1005.11(a) window)? If yes, freeze all retry and dunning attempts and route to investigation; if no active dispute, proceed to SCA/geo check.",
        "position": {
          "x": 0,
          "y": 360
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "low",
        "tools": [
          "core-ledger",
          "customer-disputes:query"
        ]
      },
      {
        "agent": "investigator",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "disputeInvestigation",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Execute Reg E error investigation: acknowledge dispute within 1–2 business days, determine within 10 business days whether an error occurred, issue provisional credit to the customer's account if investigation extends beyond day 10, complete final determination by day 45 (only if provisional credit granted), and document findings. Freeze all recovery retries and dunning during the investigation window.",
        "position": {
          "x": 0,
          "y": 480
        },
        "reversible": true,
        "sla": "10 business days (mandatory initial determination); up to 45 calendar days with provisional credit per 12 CFR § 1005.11(b)",
        "sla_spec": null,
        "tier": "medium",
        "tools": [
          "core-ledger:provisional-credit",
          "billing-platform",
          "customer-comms"
        ]
      },
      {
        "agent": "analyzer",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "scaCheck",
        "interrupt_before": false,
        "kind": "agent",
        "label": "For EU customers: validate SCA exemption status per PSD2 RTS § 13. If the initial subscription charge failed SCA validation and no exemption applies (cardholder-initiated transaction, trusted merchant, or low-risk threshold <€50), route to suspensionGate to pause recovery until customer completes a new SCA challenge. If exemption is valid or customer is non-EU, proceed to failure-type classification.",
        "position": {
          "x": 0,
          "y": 600
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "low",
        "tools": [
          "customer:geo-check",
          "sca-service:exemption-lookup"
        ]
      },
      {
        "agent": null,
        "confidential": false,
        "gate": null,
        "gateway": "exclusive",
        "human": null,
        "id": "failureType",
        "interrupt_before": false,
        "kind": "gateway",
        "label": "Failure type & recoverability?",
        "position": {
          "x": 0,
          "y": 720
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "gateway",
        "tools": []
      },
      {
        "agent": "retry",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "refreshCard",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Expired card: trigger account-updater to refresh stored credentials before retrying",
        "position": {
          "x": 0,
          "y": 840
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "medium",
        "tools": [
          "card-network:account-updater",
          "payment-gateway:retry-scheduler"
        ]
      },
      {
        "agent": null,
        "confidential": false,
        "gate": null,
        "gateway": "parallel",
        "human": null,
        "id": "recoverySplit",
        "interrupt_before": false,
        "kind": "gateway",
        "label": "Run smart retries and multi-channel dunning concurrently",
        "position": {
          "x": 0,
          "y": 960
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "gateway",
        "tools": []
      },
      {
        "agent": "retry",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "retry",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Before scheduling each retry, validate: (1) minimum 48–72 hours have elapsed since the prior attempt, and (2) total retry count remains below 7 per Visa/Mastercard network rules (Visa § 5.4.6; Mastercard Switch Rules § 10.1) to avoid chargeback exposure under codes 4855/4863. If either limit is exceeded, block retry and route to suspensionGate. Otherwise, schedule issuer- and time-zone-aware smart retries and refresh expired-card credentials via account-updater.",
        "position": {
          "x": 0,
          "y": 1080
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "medium",
        "tools": [
          "payment-gateway:retry-scheduler",
          "card-network:account-updater",
          "chargeback-risk:spacing-validator",
          "chargeback-risk:attempt-counter"
        ]
      },
      {
        "agent": "outreach",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "dunning",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Before any outreach, validate the customer has not submitted a written cease-communication notice per FDCPA § 1692c(c). If a cease notice is on file, block all communications immediately and route to suspensionGate. Otherwise, run compliant multi-channel dunning (email/SMS/in-app) with self-serve card-update links, respecting the 7-call/7-day frequency cap, the 8 AM–9 PM consumer-local-time window (§ 1692d(a)), and comms and consent limits from the consent ledger.",
        "position": {
          "x": 0,
          "y": 1200
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "medium",
        "tools": [
          "comms-outreach",
          "card-update-portal",
          "consent-ledger",
          "consent-ledger:cease-communication-check"
        ]
      },
      {
        "agent": null,
        "confidential": false,
        "gate": null,
        "gateway": "exclusive",
        "human": null,
        "id": "recovered",
        "interrupt_before": false,
        "kind": "gateway",
        "label": "Recovery outcome after the retry/dunning window?",
        "position": {
          "x": 0,
          "y": 1320
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "gateway",
        "tools": []
      },
      {
        "agent": null,
        "confidential": false,
        "gate": "gate:concession-signoff",
        "gateway": null,
        "human": "Billing & dunning policy owner (RevOps / Finance)",
        "id": "concessionGate",
        "interrupt_before": true,
        "kind": "gate",
        "label": "The billing owner signs any fee waiver, refund, or credit concession proposed to win back the payment. Before signing, confirm no open Reg E error dispute is active for this transaction — if a dispute is active, reject the concession and route to disputeInvestigation.",
        "position": {
          "x": 0,
          "y": 1440
        },
        "reversible": false,
        "sla": null,
        "sla_spec": null,
        "tier": "high",
        "tools": []
      },
      {
        "agent": "execute",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "execConcession",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Execute the approved concession (waive fee / issue credit or refund), post it to the ledger, and re-attempt the discounted invoice",
        "position": {
          "x": 0,
          "y": 1560
        },
        "reversible": false,
        "sla": null,
        "sla_spec": null,
        "tier": "high",
        "tools": [
          "billing-platform",
          "core-ledger",
          "payment-gateway:refund"
        ]
      },
      {
        "agent": null,
        "confidential": false,
        "gate": "gate:suspension-signoff",
        "gateway": null,
        "human": "Billing & dunning policy owner (RevOps / Finance)",
        "id": "suspensionGate",
        "interrupt_before": true,
        "kind": "gate",
        "label": "The billing owner signs the suspension or cancellation of the delinquent account once the recovery window is exhausted (or all retry/dunning attempts are confirmed complete), a hard decline is confirmed, an SCA challenge cannot be completed, or a cease-communication notice blocks further outreach",
        "position": {
          "x": 0,
          "y": 1680
        },
        "reversible": false,
        "sla": null,
        "sla_spec": null,
        "tier": "high",
        "tools": []
      },
      {
        "agent": "submit",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "execSuspension",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Execute the approved service suspension or subscription cancellation and notify the customer",
        "position": {
          "x": 0,
          "y": 1800
        },
        "reversible": false,
        "sla": null,
        "sla_spec": null,
        "tier": "high",
        "tools": [
          "billing-platform:cancel",
          "entitlement-service",
          "comms-outreach"
        ]
      },
      {
        "agent": "monitor",
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "monitor",
        "interrupt_before": false,
        "kind": "agent",
        "label": "Track recovered-revenue, retry-success, and involuntary-churn metrics and feed outcomes back into retry/dunning timing. Assign a data-retention-expiry date (max 6 years per GDPR § 5(1)(e)) to all failed-payment records and dunning logs, and trigger automatic deletion at expiry.",
        "position": {
          "x": 0,
          "y": 1920
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "low",
        "tools": [
          "billing-platform:analytics",
          "recovery-policy",
          "gdpr:data-retention-ledger",
          "gdpr:audit-log-cleanup"
        ]
      },
      {
        "agent": null,
        "confidential": false,
        "gate": null,
        "gateway": null,
        "human": null,
        "id": "__end__",
        "interrupt_before": false,
        "kind": "terminal",
        "label": "Done",
        "position": {
          "x": 0,
          "y": 2040
        },
        "reversible": true,
        "sla": null,
        "sla_spec": null,
        "tier": "terminal",
        "tools": []
      }
    ]
  },
  "hash": "008a03f46d2d6abedb13bb00842d5f31b48106775d8d6fc6eea9b09f55afe3dd",
  "name": "Failed-payment smart-dunning autopilot",
  "slug": "failed-payment-smart-recovery",
  "source": "example",
  "version": null,
  "violations": []
}
