Documentation · Maqaas v1.0.0

Risk Pattern Library

Every pattern Maqaas detects, what it actually concludes, and — as importantly — what it refuses to conclude.

Why “risk” and not “fraud”. Several of these are operational or data-quality conditions, not fraud signals. Calling the whole set a fraud library would overstate most of it. Only three patterns carry a plausible fraud interpretation, and even those are framed as conditions requiring verification rather than accusations.

Language used throughout. Maqaas reports a risk signal, a discrepancy, or a review condition. It never reports “fraud detected”, a “fraud probability”, or a risk score. It has no historical outcome data, so any such number would be invented.

Nothing here is model-generated. Every pattern is a deterministic rule. Identical inputs produce identical findings, forever.


The twelve patterns

Pattern IDCategoryFires as
BANK_DETAILS_CHANGEDfraud/riskHOLD_FOR_REVIEW
DUPLICATE_INVOICE_NUMBERfraud/riskHOLD_FOR_REVIEW
VENDOR_IDENTITY_MISMATCHidentityREVIEW
APPROVAL_THRESHOLDpolicyESCALATE
MISSING_POcompletenessREQUEST_MORE_INFORMATION
PO_NOT_VERIFIEDcompletenessREQUEST_MORE_INFORMATION
INVOICE_PO_VARIANCEpolicyREVIEW
CURRENCY_MISMATCHpolicyREVIEW
NOT_AN_INVOICEcompletenessREVIEW / REQUEST_MORE_INFORMATION
UNVERIFIED_IDENTIFIERdata qualityREVIEW
LOW_EXTRACTION_CONFIDENCEdata qualityREVIEW
CONFLICTING_FIELDSdata qualityREVIEW

A rule that cannot be evaluated is reported as skipped with a reason. It is never silently treated as passed — an unevaluated safety rule is uncertainty, and hiding it would overstate how much was checked.


BANK_DETAILS_CHANGED

Category: fraud/risk · Fires as: HOLD_FOR_REVIEW

What it means. The payment details on the invoice differ from the vendor’s trusted record.

Inputs required. extracted_facts.bank_details and vendor_context.trusted_bank_details. Without a trusted record the rule fires at REVIEW instead, saying there is no baseline to compare against.

What Maqaas compares. Field by field, only where both records carry a value: account_number_last4, iban_last4, sort_code, swift_bic, bank_name. Case- and whitespace-insensitive.

Evidence returned. expected and observed naming each differing field and its value, plus a count. Partial identifiers only — the schema has no field capable of holding a full account number or IBAN.

Recommended action. REQUEST_VENDOR_BANK_VERIFICATION Requirement. independent_bank_verification Default owner. VENDOR_MANAGEMENT

Example blocker.

{ "rule_id": "BANK_DETAILS_CHANGED", "severity": "HOLD_FOR_REVIEW",
  "evidence": { "expected": "account_number_last4=9021, sort_code=20-00-00",
                "observed": "account_number_last4=4471, sort_code=30-96-14",
                "comparison": "2 of 2 compared identifier(s) differ from the trusted record (normalised); bank_name_differs=false" } }

What Maqaas does NOT conclude. Not “this is fraud.” It concludes: “this change requires independent verification before processing.”

That distinction is the whole design. Vendors legitimately change banks. The signal is not “someone is stealing from you” — it is “the one change that makes invoice-redirection fraud possible has occurred, and the verification step must happen before money moves.” Maqaas also does not judge whether the new details are correct; it has no way to know.


DUPLICATE_INVOICE_NUMBER

Category: fraud/risk · Fires as: HOLD_FOR_REVIEW

What it means. This invoice number has been seen before for this vendor.

Inputs required. extracted_facts.invoice_number and vendor_context.known_invoice_numbers. Skipped without history — Maqaas stores nothing between requests, so the history is yours to supply.

What Maqaas compares. The extracted invoice number against the supplied list, under identifier normalisation.

Recommended action. VERIFY_DUPLICATE_STATUS Requirement. duplicate_status_verification Default owner. AP_REVIEWER

What Maqaas does NOT conclude. Not “this is a duplicate payment attempt.” A repeated number is very often a legitimate reissue or a credit note. The action is deliberately verify status, not request a corrected invoice — assuming vendor error would send the wrong instruction to the queue.


VENDOR_IDENTITY_MISMATCH

Category: identity · Fires as: REVIEW

What it means. The invoice arrived from a domain not recorded for this vendor.

Inputs required. message.from and vendor_context.known_domains. Skipped if either is absent or the address cannot be parsed.

What Maqaas compares. The sender’s domain against the recorded domains.

Recommended action. VERIFY_VENDOR_IDENTITY Requirement. vendor_identity_verification Default owner. VENDOR_MANAGEMENT

What Maqaas does NOT conclude. Not “this sender is an impostor.” Vendors use billing bureaux, subsidiaries, and new domains constantly. It concludes the sender is unrecognised and should be confirmed before the document is treated as genuine. Maqaas performs no SPF, DKIM, or DMARC checking — it compares a domain string to a list you supplied.


APPROVAL_THRESHOLD

Category: policy · Fires as: ESCALATE

What it means. The invoice total meets or exceeds the configured approval threshold.

Inputs required. rules.approval_threshold and an extracted invoice_total. Skipped if the threshold is not configured.

Recommended action. ROUTE_TO_APPROVER Requirement. spend_approval Default owner. APPROVER

What Maqaas does NOT conclude. Nothing is wrong with this invoice. This is the one pattern where the highest decision severity carries the most benign remediation: it needs a signature, not a correction. That is exactly why the resolution plan is ordered by remediation urgency rather than decision severity — a payment-redirection step must reach a queue ahead of a routine approval, even though the approval carries the higher severity.


MISSING_PO

Category: completeness · Fires as: REQUEST_MORE_INFORMATION

What it means. Your policy requires a purchase order and none was found on the invoice or supplied as context.

Inputs required. rules.require_po set true. Skipped otherwise.

Recommended action. REQUEST_PO_REFERENCE Requirement. purchase_order_reference Default owner. PROCUREMENT

What Maqaas does NOT conclude. It does not conclude the invoice is invalid, and it does not promise the invoice will pass once a PO arrives. Supplying a PO activates INVOICE_PO_VARIANCE and CURRENCY_MISMATCH, which were skipped rather than passed. Those appear in decision_if_resolved.rules_still_not_evaluated. See Demo 2 in PILOT_DEMO.md.


PO_NOT_VERIFIED

Category: completeness · Fires as: REQUEST_MORE_INFORMATION

What it means. The invoice prints a PO reference, but that is the vendor’s own claim and no buyer-side PO record was supplied to check it against. Your policy requires a PO, so the claim alone cannot auto-process.

Inputs required. rules.require_po set true, a po_number on the invoice, and no po_context. Skipped otherwise.

Recommended action. REQUEST_PO_REFERENCE Requirement. purchase_order_record Default owner. PROCUREMENT

Relationship to MISSING_PO. They never fire together: MISSING_PO owns the case where no reference exists at all, this rule owns the case where one exists but is unverified. Supplying a PO record silences both and activates INVOICE_PO_VARIANCE and CURRENCY_MISMATCH, which were skipped rather than passed.

What Maqaas does NOT conclude. It does not conclude the printed reference is false. It says only that nothing independent has confirmed it. Added in rules 1.3.0: an invoice citing a PO with no po_context now returns REQUEST_MORE_INFORMATION where rules 1.2.0 could return PROCESS.


INVOICE_PO_VARIANCE

Category: policy · Fires as: REVIEW

What it means. The invoice total differs from the purchase order beyond the allowed variance.

Inputs required. po_context.total, an extracted invoice_total, and rules.allowed_variance_pct. Skipped when currencies differ (deferred to CURRENCY_MISMATCH) or when the PO total is zero, where variance is undefined.

Evidence returned. The PO total, the invoice total, the computed variance, and the configured threshold — enough to reproduce the arithmetic.

Recommended action. RESOLVE_PO_VARIANCE Requirement. variance_explanation_or_correction Default owner. PROCUREMENT

What Maqaas does NOT conclude. It proves the totals differ beyond the configured tolerance. It does not prove which source is wrong, and does not propose a corrected figure. The action is deliberately neutral: naming a corrected invoice as the remedy would assert vendor fault on the strength of an arithmetic difference alone.

Four things legitimately resolve the blocker — an approved variance, an updated purchase order, a corrected invoice where the invoice really is wrong, or confirmation that partial delivery or partial billing explains the difference.

Known limitation. Detection compares invoice_total against the full PO total. Maqaas has no prior-invoiced amount or PO balance, so a legitimate partial or milestone invoice still fires this rule. The neutral action makes the guidance honest; it does not remove that false positive.


CURRENCY_MISMATCH

Category: policy · Fires as: REVIEW

What it means. The invoice is denominated in a different currency from the purchase order.

Inputs required. po_context.total.currency and an extracted invoice_total.currency.

Recommended action. REQUEST_CORRECTED_INVOICE Requirement. currency_reconciliation Default owner. PROCUREMENT

What Maqaas does NOT conclude. It performs no FX conversion and applies no exchange rate. Comparing 41,200 GBP to 41,200 USD as though a rate made them equivalent would be a decision about money that Maqaas is not entitled to make. It reports that the two disagree.


NOT_AN_INVOICE

Category: completeness · Fires as: REVIEW or REQUEST_MORE_INFORMATION

What it means. The document does not appear to be an invoice.

Inputs required. document_type and classification_confidence.

Recommended action. RECLASSIFY_DOCUMENT Requirement. document_reclassification Default owner. AP_REVIEWER

What Maqaas does NOT conclude. Not that the document is wrong or invalid. A statement, remittance, credit note or purchase order may be perfectly correct and simply belong in a different process. The action is reclassify, not correct.


UNVERIFIED_IDENTIFIER

Category: data quality · Fires as: REVIEW

What it means. An identifier was extracted with a value, but that value could not be confirmed against the document text.

Inputs required. None beyond extraction. Applies to invoice_number, vat_number, company_registration_number, vendor_id, po_number.

What Maqaas checks. Whether the identifier’s source_verified is explicitly false. Four causes produce that: the snippet was not found in the document, the value was not supported by its own snippet, the source text contained an unmappable glyph, or the identifier contains a placeholder character.

Evidence returned. The affected field names and the verification reason.

Recommended action. VERIFY_EXTRACTED_FIELDS Requirement. identifier_verification Default owner. AP_REVIEWER

What Maqaas does NOT conclude. It does not guess the correct value, repair a separator, or drop an unreadable character to make the value look clean. The instruction states explicitly that the value has not been altered. A silently “corrected” invoice number is worse than an unverified one — it looks trustworthy and is not, and it is the key a duplicate check depends on.

Note the tri-state: source_verified is true, false, or null for “could not check”. Only an explicit false fires this rule. Collapsing null into false would flood the queue; collapsing it into true would hide the case.


LOW_EXTRACTION_CONFIDENCE

Category: data quality · Fires as: REVIEW

What it means. One or more critical fields were read with confidence below your configured floor.

Inputs required. rules.min_field_confidence (default 0.8). Critical fields are invoice_number, invoice_total, vendor_name.

What Maqaas compares. The effective confidence — which is capped at 0.5 for any field that failed source verification — against the floor. A model’s self-reported certainty never raises a field above what the document supports.

Recommended action. VERIFY_EXTRACTED_FIELDS Requirement. field_verification Default owner. AP_REVIEWER

What Maqaas does NOT conclude. A low confidence is not a claim that the value is wrong. It is a claim that the value should not be relied on without a human confirming it. Maqaas does not retry, re-extract, or substitute a “better” guess.


CONFLICTING_FIELDS

Category: data quality · Fires as: REVIEW

What it means. The same fact appeared with two different values across the supplied sources — typically the email body says one thing and the attachment says another.

Inputs required. extracted_facts.conflicting_fields.

Recommended action. VERIFY_EXTRACTED_FIELDS Requirement. conflict_resolution Default owner. AP_REVIEWER

What Maqaas does NOT conclude. It does not pick a winner. It does not prefer the attachment over the body, or the higher figure over the lower. It reports that the sources disagree and asks which is authoritative.


What is deliberately not in this library

No pattern here is inferred, learned, or scored. Specifically absent:

  • Vendor behavioural baselines — “this vendor usually invoices £5k, this one is £50k”. Requires history Maqaas does not store.
  • Fraud probability or risk scores — requires labelled outcome data that does not exist.
  • Anomaly detection — no statistical model, no clustering, no learned norms.
  • Cross-tenant intelligence — nothing learned from one customer is applied to another. Maqaas keeps no invoice history; there is nothing to learn from.
  • Sanctions, PEP, or credit screening — no external data sources.
  • Email authentication — no SPF/DKIM/DMARC verification.

Some of these are reasonable future work. None of them are implemented, so none of them are claimed.