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 ID | Category | Fires as |
|---|---|---|
BANK_DETAILS_CHANGED | fraud/risk | HOLD_FOR_REVIEW |
DUPLICATE_INVOICE_NUMBER | fraud/risk | HOLD_FOR_REVIEW |
VENDOR_IDENTITY_MISMATCH | identity | REVIEW |
APPROVAL_THRESHOLD | policy | ESCALATE |
MISSING_PO | completeness | REQUEST_MORE_INFORMATION |
PO_NOT_VERIFIED | completeness | REQUEST_MORE_INFORMATION |
INVOICE_PO_VARIANCE | policy | REVIEW |
CURRENCY_MISMATCH | policy | REVIEW |
NOT_AN_INVOICE | completeness | REVIEW / REQUEST_MORE_INFORMATION |
UNVERIFIED_IDENTIFIER | data quality | REVIEW |
LOW_EXTRACTION_CONFIDENCE | data quality | REVIEW |
CONFLICTING_FIELDS | data quality | REVIEW |
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.