> ## Documentation Index
> Fetch the complete documentation index at: https://docs.herondata.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Fraud Detection & Monitoring

> Check submissions against Heron's fraud network and receive the flag, evidence, and network via API or webhook

<Note>
  **Consortium members only.** Heron's fraud network is available to members of the Heron Fraud Consortium, who report their early defaults to help fight fraudsters. It is not enabled by default - contact your Heron rep to enquire about joining the Consortium.
</Note>

Heron sees more than 60k funding submissions a day across the lenders and brokers on the platform. Fraud Detection checks each submission you send against Heron's fraud network - a graph of businesses and people linked to early defaults - and continues to monitor it after the initial check.

When a submission matches, the result contains the flag, a summary of why, the fields that matched, and the section of the network the business sits in, with evidence on its nodes and edges.

There are two signals:

| Signal              | What it detects                                                                                                            | Enricher name        |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| **Fraud network**   | The applicant, its owners, or a business linked to them has an early default (\< 10% RTR) reported by a consortium member. | `fraud_network_scan` |
| **Broker velocity** | The same merchant has been submitted by an unusual number of brokers in the last 24 hours.                                 | `broker_velocity`    |

Both run as enrichers and use the same workflow, results endpoint, and webhook mechanics as the rest of the platform.

## How it works

The fraud network is a graph with three kinds of record:

* **Nodes** - a business or a person, returned in separate `businesses` and `people` lists. Node fields use the same vocabulary as the [Background Check](/integrations/background-check) response: business `names` typed `legal` or `dba`; business `identifiers` typed `us_ein`; person `names` typed `legal`; person `national_ids` typed `us_ssn`; and an `address` with `line_1`, `line_2`, `city`, `region`, `postcode`, `country` (`US`). EINs and SSNs are masked to their last four digits (`XX-XXX1234`, `XXX-XX-1234`).
* **Edges** - a relationship between two nodes: `owner_of` (a person owns a business; always runs person -> business) or `transacts_with` (two businesses move money between each other). Shared ownership is expressed as a person node with an `owner_of` edge to each business.
* **Evidence** - the reason a node or edge is in the graph. Each piece has a `kind`, a `source` fixed by the kind, an `occurred_at` date (or `null`), and a kind-specific `detail`.

| Evidence kind             | `source`                   | What it means                                                                     | `detail`                                                                                                                                 |
| ------------------------- | -------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `customer_reported_fraud` | `consortium_member_report` | A consortium member reported this business as an early default (\< 10% RTR).      | `{"first_payment_default": {"advance_date": "YYYY-MM-DD"}}`, or `{}` when no advance date was reported.                                  |
| `public_record`           | `court_records`            | Court cases involving this business. `is_mca` marks suits brought by MCA funders. | `{"public_record": [{"is_mca": true}, ...]}` - one entry per case. Case numbers, names and links are not returned.                       |
| `detected_money_flow`     | `heron_bank_data`          | Transfers between two businesses.                                                 | `{"strength": "weak"}` or `{"strength": "strong"}`, plus `transaction_references` when every transaction came from your own submissions. |
| `detected_funding`        | `heron_bank_data`          | An MCA advance received by this business.                                         | `{"strength": ...}` as above.                                                                                                            |

Every addition to the network is reviewed by a Heron analyst before it is used for matching.

A match is classified by the shape of the network around it:

* **`lone_actor`** - one owner behind one or more businesses, with no links to businesses under different ownership.
* **`fraud_network`** - linked businesses under different owners.

Matching uses the submission's legal name, DBA, EIN, address, owner names, and owner SSNs. The `match_meta` block in the result lists the fields a match was based on.

## Scanning a submission

### Prerequisites

* An API key (see [Authentication](/api-reference/authentication)). All requests send it in the `x-api-key` header.
* Fraud Detection enabled for your account.
* An end user representing the submission. One is created automatically whenever you send Heron an ISO application, or you can create one directly ([Create EndUser](/api-reference/endusers/create-enduser)).

### 1. Make sure the submission's identity has reached Heron

The scan matches on the owner and business information Heron holds for the submission. If you send PDF ISO applications, Heron extracts this automatically. Otherwise supply it with `PATCH /end_users/{id}/end_user_information` ([reference](/api-reference/endusers/patch-enduser-information)) - the same fields the [Background Check](/integrations/background-check#1-make-sure-the-deals-details-have-reached-heron) uses.

<Note>
  EIN and owner SSN produce the highest-confidence matches. The `input_coverage` block in every result shows which fields were available, so you can see how much identity a result was based on.
</Note>

### 2. Run the scan

If the enricher is in your Heron workflow it runs automatically on every submission, and its result is available to your policy checks so a match can be routed to review or declined in the workflow. To trigger it directly:

```python app.py theme={null}
import requests

BASE_URL = "https://app.herondata.io"
API_KEY = "key_xxxxxxxx"
end_user_heron_id = "eus_xxxxxxxx"  # the submission you're screening

requests.post(
  f"{BASE_URL}/api/end_user_enrichers/definitions/fraud_network_scan/start_enrich",
  headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
  json={"end_user_heron_id": end_user_heron_id},
).raise_for_status()
```

It returns `202 Accepted` as soon as the run is dispatched. Subscribe to the `enricher.succeeded` webhook topic to be notified when it finishes, or poll.

### 3. Fetch the result

The result has its own endpoint, `GET /end_users/{id}/fraud_detection/fraud_network`. It returns `404` until a scan has run.

```python app.py theme={null}
import requests

BASE_URL = "https://app.herondata.io"
API_KEY = "key_xxxxxxxx"
end_user_heron_id = "eus_xxxxxxxx"

response = requests.get(
  f"{BASE_URL}/api/end_users/{end_user_heron_id}/fraud_detection/fraud_network",
  headers={"x-api-key": API_KEY},
)
response.raise_for_status()
scan = response.json()

if scan["status"] == "match":
    print(scan["classification"], "-", scan["summary"]["headline"])
    print(scan["summary"]["network"])
    print(scan["summary"]["this_business"])
```

## Understanding the result

Every result has the same top-level shape whether or not anything matched.

| Field                           | Meaning                                                                                                                |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `status`                        | `match`, `no_match`, or `insufficient_input` (not enough identity data to scan).                                       |
| `classification`                | `fraud_network` or `lone_actor` on a match; `null` otherwise.                                                          |
| `scanned_at`                    | When this scan ran.                                                                                                    |
| `monitoring_until`              | Heron keeps re-scanning this submission until this time (see [Ongoing monitoring](#ongoing-monitoring)).               |
| `input_coverage`                | Which identity fields were available to match on.                                                                      |
| `summary`                       | Plain-English lines describing the match.                                                                              |
| `network`                       | Counts for the network the match belongs to. `null` for lone actors and no-match.                                      |
| `matched_node_heron_id`         | The node your submission matched. When more than one node matched, the one closest to a confirmed default.             |
| `match_meta`                    | The fields the match was based on, a confidence score, and how many hops the matched node is from a confirmed default. |
| `businesses`, `people`, `edges` | The section of the graph within three hops of any matched node, with evidence. Empty when there is no match.           |

### A fraud network match

```json theme={null}
{
  "status": "match",
  "classification": "fraud_network",
  "scanned_at": "2026-09-11T10:02:00Z",
  "monitoring_until": "2026-10-11T10:02:00Z",
  "input_coverage": {
    "legal_name": true, "dba": true, "ein": true, "address": true,
    "owner_name": true, "owner_ssn": true
  },
  "summary": {
    "headline": "Fraud Network Sighting",
    "network": "6 members linked by money movement + shared ownership; 2 early defaults; 3 with MCA suits on public record; reported as fraud by 2 consortium members",
    "this_business": "1 hop from a defaulted member · 1 MCA suit on record · moves money with 2 network members · shares an owner with 1 member",
    "why_listed": null,
    "seen_elsewhere": "Applied to 3 other lenders in the last 30 days",
    "matched_on": "DBA, EIN, owner name, owner SSN"
  },
  "network": {
    "heron_id": "fnw_xxxxxxxx",
    "member_count": 6,
    "first_payment_default_count": 2,
    "mca_suit_member_count": 3,
    "reporting_member_count": 2
  },
  "matched_node_heron_id": "fnn_10",
  "match_meta": {
    "score": 0.91,
    "matched_fields": [
      { "subject": "business", "key": "dba", "value": "Atlas Holdings" },
      { "subject": "business", "key": "ein", "value": "XX-XXX9034" },
      { "subject": "owner_1", "key": "owner_name", "value": "Maria Lopez" },
      { "subject": "owner_1", "key": "ssn", "value": "XXX-XX-8802" }
    ],
    "distance_to_first_payment_default": 1
  },
  "businesses": [
    {
      "heron_id": "fnn_10",
      "redacted": false,
      "names": [
        { "raw": "Atlas Holdings Group LLC", "type": "legal" },
        { "raw": "Atlas Holdings", "type": "dba" }
      ],
      "identifiers": [ { "type": "us_ein", "value": "XX-XXX9034", "country": "US" } ],
      "type_of_entity": "llc",
      "address": { "line_1": "9 Harbor Rd", "line_2": null, "city": "Newark", "region": "NJ", "postcode": "07102", "country": "US" },
      "evidence": [
        {
          "heron_id": "fnv_202",
          "kind": "public_record",
          "source": "court_records",
          "occurred_at": "2026-07-30",
          "detail": { "public_record": [ { "is_mca": true } ] }
        }
      ],
      "sightings": [
        { "end_user_heron_id": "eus_xxxxxxxx", "seen_at": "2026-09-11T10:02:00Z", "role": null }
      ]
    },
    {
      "heron_id": "fnn_11",
      "redacted": true,
      "names": [],
      "identifiers": [],
      "type_of_entity": null,
      "address": null,
      "evidence": [
        {
          "heron_id": "fnv_203",
          "kind": "customer_reported_fraud",
          "source": "consortium_member_report",
          "occurred_at": "2026-08-18",
          "detail": { "first_payment_default": { "advance_date": "2026-06-02" } }
        },
        {
          "heron_id": "fnv_205",
          "kind": "public_record",
          "source": "court_records",
          "occurred_at": "2026-05-12",
          "detail": { "public_record": [ { "is_mca": true }, { "is_mca": true } ] }
        }
      ],
      "sightings": []
    }
  ],
  "people": [
    {
      "heron_id": "fnn_13",
      "redacted": false,
      "names": [ { "raw": "Maria Lopez", "prefix": null, "given": "Maria", "middle": null, "surname": "Lopez", "suffix": null, "type": "legal" } ],
      "national_ids": [ { "type": "us_ssn", "value": "XXX-XX-8802", "country": "US" } ],
      "address": { "line_1": "300 Park Pl", "line_2": null, "city": "Brooklyn", "region": "NY", "postcode": "11238", "country": "US" },
      "evidence": [],
      "sightings": [
        { "end_user_heron_id": "eus_xxxxxxxx", "seen_at": "2026-09-11T10:02:00Z", "role": "owner_1" }
      ]
    },
    {
      "heron_id": "fnn_14",
      "redacted": true,
      "names": [],
      "national_ids": [],
      "address": null,
      "evidence": [],
      "sightings": []
    }
  ],
  "edges": [
    {
      "heron_id": "fne_20",
      "edge_type": "transacts_with",
      "from_node_heron_id": "fnn_10",
      "to_node_heron_id": "fnn_11",
      "evidence": [
        { "heron_id": "fnv_206", "kind": "detected_money_flow", "source": "heron_bank_data", "occurred_at": null, "detail": { "strength": "strong" } }
      ]
    },
    {
      "heron_id": "fne_22",
      "edge_type": "owner_of",
      "from_node_heron_id": "fnn_13",
      "to_node_heron_id": "fnn_10",
      "evidence": []
    },
    {
      "heron_id": "fne_24",
      "edge_type": "owner_of",
      "from_node_heron_id": "fnn_14",
      "to_node_heron_id": "fnn_11",
      "evidence": []
    }
  ]
}
```

In this example the applicant (`fnn_10`) moves money with a business another member reported as a default (`fnn_11`). `fnn_11` and its owner `fnn_14` are redacted: you can see they exist, their evidence and how they connect, but not who they are.

### A lone actor match

```json theme={null}
{
  "status": "match",
  "classification": "lone_actor",
  "scanned_at": "2026-09-11T10:02:00Z",
  "monitoring_until": "2026-10-11T10:02:00Z",
  "input_coverage": {
    "legal_name": true, "dba": true, "ein": true, "address": true,
    "owner_name": true, "owner_ssn": true
  },
  "summary": {
    "headline": "Lone Actor Sighting",
    "network": null,
    "this_business": "Confirmed early default · 5 MCA suits on record",
    "why_listed": "Reported to us as an early default by a consortium member. Advanced Jul 2026, written off.",
    "seen_elsewhere": "Applied to 2 other lenders in the last 30 days",
    "matched_on": "legal name, EIN, owner SSN"
  },
  "network": null,
  "matched_node_heron_id": "fnn_1",
  "match_meta": {
    "score": 0.97,
    "matched_fields": [
      { "subject": "business", "key": "legal_name", "value": "Company A LLC" },
      { "subject": "business", "key": "ein", "value": "XX-XXX4821" },
      { "subject": "owner_1", "key": "ssn", "value": "XXX-XX-4417" }
    ],
    "distance_to_first_payment_default": 0
  },
  "businesses": [
    {
      "heron_id": "fnn_1",
      "redacted": false,
      "names": [
        { "raw": "Company A LLC", "type": "legal" },
        { "raw": "Company A Trucking", "type": "dba" }
      ],
      "identifiers": [ { "type": "us_ein", "value": "XX-XXX4821", "country": "US" } ],
      "type_of_entity": "llc",
      "address": { "line_1": "120 Main St", "line_2": null, "city": "Houston", "region": "TX", "postcode": "77002", "country": "US" },
      "evidence": [
        {
          "heron_id": "fnv_101",
          "kind": "customer_reported_fraud",
          "source": "consortium_member_report",
          "occurred_at": "2026-09-01",
          "detail": { "first_payment_default": { "advance_date": "2026-07-14" } }
        },
        {
          "heron_id": "fnv_102",
          "kind": "public_record",
          "source": "court_records",
          "occurred_at": "2026-08-20",
          "detail": {
            "public_record": [
              { "is_mca": true }, { "is_mca": true }, { "is_mca": true }, { "is_mca": true }, { "is_mca": true }
            ]
          }
        }
      ],
      "sightings": [
        { "end_user_heron_id": "eus_xxxxxxxx", "seen_at": "2026-09-11T10:02:00Z", "role": null }
      ]
    }
  ],
  "people": [
    {
      "heron_id": "fnn_2",
      "redacted": false,
      "names": [ { "raw": "John Smith", "prefix": null, "given": "John", "middle": null, "surname": "Smith", "suffix": null, "type": "legal" } ],
      "national_ids": [ { "type": "us_ssn", "value": "XXX-XX-4417", "country": "US" } ],
      "address": { "line_1": "44 Elm Ave", "line_2": null, "city": "Houston", "region": "TX", "postcode": "77006", "country": "US" },
      "evidence": [],
      "sightings": [
        { "end_user_heron_id": "eus_xxxxxxxx", "seen_at": "2026-09-11T10:02:00Z", "role": "owner_1" }
      ]
    }
  ],
  "edges": [
    {
      "heron_id": "fne_1",
      "edge_type": "owner_of",
      "from_node_heron_id": "fnn_2",
      "to_node_heron_id": "fnn_1",
      "evidence": []
    }
  ]
}
```

### No match

```json theme={null}
{
  "status": "no_match",
  "classification": null,
  "scanned_at": "2026-09-11T10:02:00Z",
  "monitoring_until": "2026-10-11T10:02:00Z",
  "input_coverage": {
    "legal_name": true, "dba": false, "ein": true, "address": true,
    "owner_name": true, "owner_ssn": true
  },
  "summary": {
    "headline": "No fraud network match",
    "network": null,
    "this_business": "Legal name, EIN, address, owner name and owner SSN checked against Heron's fraud network. No match.",
    "why_listed": null,
    "seen_elsewhere": null,
    "matched_on": null
  },
  "network": null,
  "matched_node_heron_id": null,
  "match_meta": null,
  "businesses": [],
  "people": [],
  "edges": []
}
```

The submission stays under monitoring until `monitoring_until`.

## Ongoing monitoring

A scan reflects the fraud network at the moment it runs. Heron keeps re-scanning each submission until `monitoring_until`, so a business that is reported by another member or linked into a network after you received it is still surfaced.

New sightings are delivered by the `fraud_network.sighting` webhook. Add one in the dashboard (Settings -> Webhooks) or via the [webhooks API](/api-reference/webhooks). It fires both when the enricher matches in your workflow and when a later re-scan matches a submission you already hold. The `data` block is the same object the fraud network endpoint returns.

```json webhook.json theme={null}
{
  "topic": "fraud_network.sighting",
  "created": "2026-09-11T10:02:14+00:00",
  "data": { "...": "the fraud_network_scan result object" },
  "meta": { "end_user_heron_id": "eus_xxxxxxxx", "end_user_id": "your-end-user-id" }
}
```

The length of the monitoring window is configured per account.

## What you see, and what you don't

The Consortium exists to fight fraud while always preserving privacy. Each response is filtered to what the requesting account is entitled to see. The rules, applied on every response:

* **Only the graph around your match is returned** - every live node within three hops of any matched node (a scan can match more than one, e.g. the business by EIN and the owner by SSN), and the edges between them. A node that is not eligible to match a new submission still appears, redacted when you have not submitted it.
* **Businesses and people you have submitted to Heron** are shown using the identity you sent us, never the consortium's copy. EINs and SSNs are masked to their last four digits. In the fraud network example above, `fnn_10` and `fnn_13` are your applicant and its owner.
* **Everything else is redacted** - `redacted: true`, `names`, `identifiers`, `national_ids` and `sightings` empty, `address` `null`. You can see that a node exists, what kind it is, its evidence, and how it connects, but not who it is. `fnn_11` above is another member's defaulted merchant: you know it defaulted, received an advance in June, moves money with your applicant, and has two MCA suits. Its name and address are not shown, so it cannot be looked up elsewhere.
* **Other members are never named.** Reports appear as "a consortium member" or a count. `sightings` on a node list only your own end users; other lenders appear only as the count in `summary.seen_elsewhere`. The free-text provenance a member wrote when contributing a record is never returned - `source` is always one of the fixed values above.
* **Public records are one `is_mca` flag per case**, on every node. Case numbers, case names and links are not returned, because a case name identifies the business.
* **Detection evidence** carries `strength` when Heron has scored it. Underlying `transaction_references` are included only when every transaction behind the detection came from your own submissions.

Heron IDs (`fnn_`, `fne_`, `fnv_`, `fnw_`) are stable, so you can track the same node across submissions and over time.

## Broker velocity

Broker velocity is a separate signal for a different pattern: the same merchant being submitted to many funders at once by many distinct brokers. It measures how many brokers have submitted the same merchant in the last 24 hours across the Heron platform, and reports a band. A `high` signal means the submission is in the top 1% of submissions on the platform by number of distinct brokers working the deal. The next 1% is `medium` and a further 1-2% is `low`.

```json theme={null}
{ "broker_velocity_signal": "high" }
```

| `broker_velocity_signal` | Meaning                                                    |
| ------------------------ | ---------------------------------------------------------- |
| `high`                   | Top 1% of submissions by number of distinct brokers.       |
| `medium`                 | The next 1%.                                               |
| `low`                    | A further 1-2%.                                            |
| `excluded_sole_prop`     | Not measured: sole proprietors are excluded.               |
| `null`                   | Below the `low` band, or the signal could not be returned. |

Trigger it with the `broker_velocity` enricher and read it from `GET /end_users/{id}/fraud_detection/broker_velocity`. The response is the band only; the underlying list of brokers or the number of hits is not shared.
