Fraud Analysis

Fraud analysis looks for signs that a PDF was tampered with or forged. When a Fraud Analyzer node is included in your workflow, the workflow run response includes risk scoring, risk factors, and trust indicators.

Overview

Fraud analysis examines each uploaded document. Its individual signals are grouped into findings: risk factors that raise suspicion and trust factors that support authenticity.

Fraud analysis results appear in two places:

  • Workflow run response: the fraud_analysis_result field at the root level of the GET workflow run response, alongside extraction_result
  • Node webhook: the node-run.completed event for fraudAnalyzer nodes includes a summary with risk_level, risk_score, and signal_count

Workflow Run Response

When a workflow includes a Fraud Analyzer node, the GET workflow run response includes a root-level fraud_analysis_result field:

GET /api/v1.1/workflow-runs/{run_id}

{
  "run_id": "wr_abc123xyz789",
  "status": "completed",
  "classification_result": { ... },
  "extraction_result": { ... },
  "fraud_analysis_result": {
    "status": "completed",
    "risk_level": "warning",
    "risk_score": 42.5,
    "risk_factors": [
      {
        "id": "content_tampering",
        "label": "Document Content Modified",
        "severity": "high",
        "details": "The document shows signs of post-creation editing...",
        "sources": ["forensic"]
      },
      {
        "id": "metadata_anomalies",
        "label": "Metadata Inconsistencies",
        "severity": "medium",
        "details": "The PDF metadata contains contradictory information...",
        "sources": ["forensic"]
      }
    ],
    "trust_factors": [
      {
        "id": "legitimate_producer",
        "label": "Known Bank Software",
        "passed": true,
        "details": "The document was generated by software commonly used by banks.",
        "sources": ["forensic"]
      }
    ]
  }
}

If no Fraud Analyzer node ran in the workflow, fraud_analysis_result is null.

When your organization limits fraud analysis to native PDFs or to bank statements (set up by Holofin on request), a document outside those filters is not analysed or billed for fraud. Its fraud_analysis_result is {"status": "skipped", "skip_reason": "not_native_pdf", "risk_level": null, "risk_score": null, "risk_factors": [], "trust_factors": []}, with skip_reason"not_native_pdf" (a scan, a photo or an image upload) or "not_bank_statement". Treat it as "not analysed", not as a clean document.

fraud_analysis_result Object

FieldTypeDescription
statusstring"completed", or "skipped" when your organization's fraud filters left the document out.
skip_reasonstringOnly when status is "skipped": "not_native_pdf" or "not_bank_statement".
risk_levelstringOverall risk assessment: "trusted", "normal", "warning", or "high_risk". null when skipped.
risk_scorenumberNumeric risk score from 0 to 100. Higher means more suspicious.
risk_factorsarrayDetected risk indicators. Each factor represents a forensic finding that increases risk.
trust_factorsarrayTrust indicators. Each factor represents a positive signal about document authenticity.
[].sourcesarray of stringsOn every risk and trust factor: where its evidence comes from. "forensic": a test on the file itself. "template": a comparison with documents the issuer is known to produce. "content": the document's own figures disagree. "seal": the signed 2D-Doc seal. A factor can have several.

Risk Levels

LevelScore RangeMeaning
trusted0 – 9No suspicious indicators detected. Document appears authentic.
normal10 – 29Minor anomalies found, likely benign.
warning30 – 59Significant anomalies that warrant manual review.
high_risk60 – 100Strong evidence of tampering or forgery. Document should be verified through alternative channels.

Risk Factor Object

FieldTypeDescription
idstringUnique finding identifier (e.g., "content_tampering", "metadata_anomalies")
labelstringHuman-readable title for the finding
severitystringSeverity level: "low", "medium", "high", or "critical"
detailsstring | nullAI-generated explanation of what was detected and why it matters

Pages redrawn by AI

The risk factor ai_page_redrawn (label « Page redrawn by AI », severity high) means a page looks redrawn by an AI image generator to change a figure, a name or a date.

What to do: do not rely on the figures of the flagged page, and ask for the document from its issuer.

Trust Factor Object

FieldTypeDescription
idstringUnique finding identifier (e.g., "legitimate_producer", "consistent_metadata")
labelstringHuman-readable title for the trust indicator
passedbooleantrue if the trust check passed, false if the check could not confirm trust
detailsstring | nullAI-generated explanation of the trust indicator

Node Webhook Payload

When a fraudAnalyzer node completes, a node-run.completed webhook is sent with a summary in the result field:

Fraud Analyzer Node Completed

{
  "id": "whev_abc123def456",
  "event": "node-run.completed",
  "created_at": "2025-06-02T14:30:00Z",
  "data": {
    "object": {
      "run_id": "wr_xyz789ghi012",
      "workflow_id": "wf_abc123def456",
      "document_id": "doc_abc123def456",
      "node_id": "fraud_analyzer_1",
      "node_type": "fraudAnalyzer",
      "status": "COMPLETED",
      "timestamp": "2025-06-02T14:30:00Z",
      "result": {
        "analysis_id": "pda_abc123def456",
        "risk_level": "warning",
        "risk_score": 42.5,
        "signal_count": 5,
        "categories_triggered": ["tampering", "metadata_anomaly"],
        "signal_summary": {
          "tampering": 2,
          "metadata_anomaly": 3
        }
      }
    }
  }
}

The node webhook provides a quick summary. For the full risk and trust factors, use the fraud_analysis_result field in the workflow run response.

FieldTypeDescription
analysis_idstringUnique analysis identifier (prefix: pda_)
risk_levelstringOverall risk level: "trusted", "normal", "warning", or "high_risk"
risk_scorenumberNumeric risk score (0–100)
signal_countintegerTotal number of forensic signals triggered
categories_triggeredarraySignal categories that had triggers (e.g., "tampering", "metadata_anomaly")
signal_summaryobjectCount of triggered signals per category