Financial audit

Test transaction-level assertions, investigate control exceptions and retain the supporting evidence behind an accounts-payable review.

For finance operations, internal audit and external auditors.

Where it fits in your workflow

Use this when a supplier invoice is ready for payment, a payment release needs an independent check, vendor bank details change, or an audit team reviews a transaction population. Supply the invoice and the records that establish its amount, counterparty, applicable controls and comparison history.

  1. Define the assertion and control. Choose the transaction or decision to test. An invoice review can check arithmetic, duplicate criteria, supplied vendor status and whether a purchase-order reference is present. An invoice-approval decision separately checks authority, independence and whether the invoice supports approval; it does not check approval timing.
  2. Assemble supporting evidence. Include invoice lines, vendor master data, relevant comparison invoices and the period under review. For substantive testing, retain the original invoice and any purchase order, receipt or delivery evidence in your audit workpapers; the API checks the structured inputs you supply.
  3. Assess materiality and follow up. The reviewer sets materiality and the nature of follow-up in the engagement. An amount, repeated exception or control deficiency can inform that judgment. Materiality is not an automatic engine setting that suppresses a failed assertion or changes a false verdict.
  4. Keep a traceable audit trail. Route the exception to its control owner, record the supporting evidence and resolution, and keep the returned assertions, input hash and configuration with the review. Approval and payment actions remain in the finance system.

What to send

  • Supplier invoice. Stable item ID, vendor_id, invoice_number, invoice_date, amount as a decimal string and currency. Supply line_items with quantity and unit_price for the arithmetic check, and a po_number when the invoice has a purchase-order reference.
  • Counterparty and people. Vendor status and master data, plus the people who entered, approved or released a transaction where those checks apply. Employee records support the expense and independence checks for the relevant item types.
  • Rules and authority. Use the pack's po_thresholds parameter, one amount per currency, for the purchase-order requirement; an invoice in a currency it does not list holds only with a PO reference. invoice_round_units and invoice_round_minimums set the round-amount indicator per currency. Approval limits, approval authority, role assignments and bank-change records belong in context.references for the checks that use them.
  • Comparison population and period. Related invoices, payments or approvals in context.records, plus the engagement period. An empty list states there are no comparison records in the supplied scope; it does not prove that no other records exist in the business.

Use POST /api/v1/verify for one item or POST /api/v1/audits for a population with shared context. See the request structure.

A $24,800 supplier invoice without a purchase-order reference

The synthetic Northstar IT Supply invoice INV-2026-0841 lists 20 laptops at $1,240.00 each. Its $24,800.00 total reconciles, the supplied vendor is active, and the request provides an empty comparison-invoice population. No purchase-order reference is supplied, although the applicable PO threshold is $1,000.00.

The po_present assertion fails and the overall verdict is false: the invoice is above the PO threshold without the required reference. That is a specific accounts-payable control exception, not a conclusion that the goods are fictitious or the total is misstated. The reviewer obtains the purchase order or documents the exception with the control owner, then assesses its significance and any further substantive testing.

Recorded verdict: false. The table below comes from the saved response.

CheckRecorded outcomeFacts or explanation
No other invoice from the vendor repeats it.holds{}
Lines, tax and discount reconcile to the total.holds{"computed": "24800.00", "stated": "24800.00"}
The vendor exists and was active on the invoice date.holds{}
A PO is referenced when the amount requires one.failsNo PO for an invoice of 24800.00, above the threshold of 1000.00.
It describes specific goods or services.not_askedNot asked: a deterministic test already fails this item, so the model cannot change its verdict.

Synthetic example. Response computed by the local engine in the production configuration. This page displays a saved example, not a new verification request. Sensitive identifiers are masked in public examples.

Inspect the request and full recorded response

Request

{
  "use_case": "financial-audit",
  "item": {
    "type": "invoice",
    "id": "INV-2026-0841",
    "data": {
      "vendor_id": "V-1042",
      "invoice_number": "INV-2026-0841",
      "invoice_date": "2026-09-18",
      "amount": "24800.00",
      "currency": "USD",
      "description": "20 business laptops for the finance team equipment refresh.",
      "line_items": [
        {
          "quantity": "20",
          "unit_price": "1240.00",
          "description": "Business laptop, 16 GB RAM, 512 GB SSD"
        }
      ],
      "tax_amount": "0.00",
      "discount_amount": "0.00"
    }
  },
  "context": {
    "records": {
      "vendor": [
        {
          "id": "V-1042",
          "name": "Northstar IT Supply",
          "created_on": "2024-02-12",
          "status": "active"
        }
      ],
      "invoice": []
    },
    "engagement": {
      "entity": "Illustrative procurement audit",
      "period": {"start": "2026-07-01", "end": "2026-09-30"}
    }
  }
}

Response

{
  "id": "ver_e8d57ccdc33a72cc",
  "object": "verification",
  "created_at": "2026-10-07T06:12:32Z",
  "use_case": "financial-audit@4",
  "item": {"type": "invoice", "id": "INV-2026-0841"},
  "verdict": "false",
  "probability_true": "0",
  "probability_basis": "deterministic",
  "certainty": {
    "value": "0.9987",
    "basis": "holdout",
    "stratum": "false_deterministic",
    "bound": "0.00133",
    "measured_on": 2253,
    "errors": 0
  },
  "undecided": [],
  "assertions": [
    {
      "id": "not_duplicate",
      "test": "procedure:not_duplicate@4",
      "outcome": "holds",
      "error_bound": "0",
      "evidence": ["/item/data/invoice_number", "/item/data/amount"]
    },
    {
      "id": "arithmetic",
      "test": "procedure:arithmetic@4",
      "outcome": "holds",
      "error_bound": "0",
      "facts": {"computed": "24800.00", "stated": "24800.00"},
      "evidence": ["/item/data/line_items", "/item/data/amount"]
    },
    {
      "id": "vendor_active",
      "test": "procedure:vendor_active@4",
      "outcome": "holds",
      "error_bound": "0",
      "evidence": ["/context/records/vendor/0/status", "/item/data/invoice_date"]
    },
    {
      "id": "po_present",
      "test": "procedure:po_present@4",
      "outcome": "fails",
      "error_bound": "0",
      "message": "No PO for an invoice of 24800.00, above the threshold of 1000.00.",
      "facts": {"required": true},
      "evidence": ["/item/data/amount", "/item/data/po_number"]
    },
    {
      "id": "goods_specific",
      "test": "question:invoice_specific@1",
      "outcome": "not_asked",
      "message": "Not asked: a deterministic test already fails this item, so the model cannot change its verdict.",
      "facts_given": {"line_count": 1, "po_present": false},
      "evidence": ["/item/data/description", "/item/data/line_items"]
    }
  ],
  "indicators": [
    {
      "id": "round_amount@2",
      "family": "amount_shape",
      "points": "0.5",
      "facts": {"amount": "24800.00"},
      "evidence": ["/item/data/amount"]
    }
  ],
  "risk": {
    "points": "1.10",
    "probability": "0.0060",
    "basis": "prior",
    "base_rate": "0.002",
    "reasons": [
      {"source": "assertion:po_present", "points": "0.60"},
      {"source": "indicator:round_amount@2", "points": "0.50"}
    ]
  },
  "scope": {
    "stratum": "false_deterministic",
    "evidence": "supplied",
    "skipped": [],
    "certainty_target": "0.995",
    "deterministic_only_verdict": "false"
  },
  "configuration": "cfg_c924a60e74c0",
  "input_sha256": "e029d0fb9aaca2da01aadba1467472ac90c6f3c293c2df0c72ce273ff2fc0019",
  "usage": {"questions_asked": 0, "questions_cached": 0, "model_ms": 0}
}

Supported records and checks

These definitions are generated from the registered use-case pack. A procedure computes a result from the supplied data; a question can decide a check only when it is qualified for the model and meets its thresholds. The descriptions name the checks; they do not authenticate the underlying business events. Supplied fields, defaults and comparison populations determine what each procedure can establish. Risk weights help prioritize review and are not probabilities.

  • expense: An employee expense claim.
  • approve_expense: Approval of an expense claim.
  • vendor: A vendor master record.
  • create_vendor: Creation of a vendor record.
  • change_vendor_bank: A change to a vendor's bank account.
  • invoice: A vendor invoice.
  • approve_invoice: Approval of an invoice.
  • payment: A payment to a vendor.
  • release_payment: Release of a payment.

expense

An employee expense claim. Required data fields: employee_id, date, amount, currency, category.

AssertionRequiresDecided byRisk weight
within_limitThe amount is within the approval limit for its category and the employee's grade.procedure1.0
receipt_presentA receipt is attached when the amount requires one.procedure0.6
not_duplicateNo other claim by the employee repeats it.procedure2.5
not_sharedNo other employee claimed the same receipt.procedure1.8
not_splitIt is not part of a claim split to stay under the limit.procedure2.0
within_employmentIt falls within the claimant's employment.procedure3.0
within_periodIt falls within the audit period.procedure0.5
business_expenseIt is a business expense.question business_expense@14.0
category_correctThe claimed category fits what was bought.procedure, then question expense_category@2 if undecided1.0
IndicatorFires whenFamilyWeight
near_limit@2Within 5% below a limit in the claim's currency.threshold0.5
near_limit_habit@2The claimant has at least three claims just below a limit in their own currency, and their share of near-limit claims is at least three times the population's. Both shares count the claims with a limit in their own currency or no limit; a claim whose limit is in another currency is in neither.threshold1.4
round_amount@2A round amount outside per diem and mileage, by the unit and minimum for its currency.amount_shape0.4
weekend_or_holiday@1Incurred on a weekend or holiday, outside exempt categories.timing0.4
stale_claim@1Submitted more than 90 days after the expense.timing0.3
employee_category_outlier@2Far above peers in the same category, grade and currency.amount_shape0.8
personal_terms@1The merchant or description uses personal-spend terms.text1.0
embedded_instruction@1The description addresses a reviewer or system with an instruction.instruction1.5

approve_expense

Approval of an expense claim. Required data fields: actor, at, on, outcome.

AssertionRequiresDecided byRisk weight
approver_authorizedThe approver's authority covers the amount.procedure1.5
approver_independentThe approver is independent of the claimant.procedure3.0
approved_before_paymentApproval came before payment.procedure1.0
claim_supports_approvalThe claim verifies true, or a justified exception is recorded.procedure, then question exception_justified@2 if undecided1.5

vendor

A vendor master record. Required data fields: name.

AssertionRequiresDecided byRisk weight
independent_of_employeesThe vendor shares no bank account, tax id, phone, email or address with an employee.procedure3.0
master_data_completeTax id, address and bank account are on file.procedure0.5

create_vendor

Creation of a vendor record. Required data fields: actor, at, on, outcome.

AssertionRequiresDecided byRisk weight
creator_authorizedThe creator held a vendor-maintenance role.procedure1.0
creator_not_payerThe creator did not enter, approve or release the vendor's transactions.procedure2.0

change_vendor_bank

A change to a vendor's bank account. Required data fields: actor, at, on, outcome.

AssertionRequiresDecided byRisk weight
change_verifiedSomeone other than the requester verified the change through an independent channel.procedure2.5
no_payment_before_verificationNo payment went out between the change and its verification.procedure3.0

invoice

A vendor invoice. Required data fields: vendor_id, invoice_number, invoice_date, amount, currency.

AssertionRequiresDecided byRisk weight
not_duplicateNo other invoice from the vendor repeats it.procedure2.5
arithmeticLines, tax and discount reconcile to the total.procedure1.0
vendor_activeThe vendor exists and was active on the invoice date.procedure1.6
po_presentA PO is referenced when the amount requires one.procedure0.6
goods_specificIt describes specific goods or services.procedure, then question invoice_specific@1 if undecided0.7
IndicatorFires whenFamilyWeight
vendor_amount_outlier@2Far above the vendor's other invoices in the same currency.amount_shape0.8
vendor_first_digit@3The first digits of the vendor's invoices in one currency depart from Benford's distribution (chi-square above the 0.1% critical value, at least 50 invoices).amount_shape0.7
sequential_vendor_invoices@1Consecutive invoice numbers over months: this buyer may be the vendor's only customer.vendor_profile1.2
round_amount@2A round invoice amount, by the unit and minimum for its currency.amount_shape0.5
embedded_instruction@1The description addresses a reviewer or system with an instruction.instruction1.5

approve_invoice

Approval of an invoice. Required data fields: actor, at, on, outcome.

AssertionRequiresDecided byRisk weight
approver_authorizedThe approver's authority covers the amount.procedure1.5
approver_independentThe approver neither entered the invoice nor created the vendor.procedure2.0
invoice_supports_approvalThe invoice verifies true, or a justified exception is recorded.procedure, then question exception_justified@2 if undecided1.5

payment

A payment to a vendor. Required data fields: vendor_id, payment_date, amount, currency.

AssertionRequiresDecided byRisk weight
matchedIt pays invoices of the same vendor within their open balance.procedure1.2
not_duplicateNo other payment of the same amount covers the same invoice.procedure2.5
to_vendor_accountIt went to the vendor's master bank account.procedure2.0
not_to_employeeThe receiving account belongs to no employee.procedure3.0
after_invoiceIt was made on or after the invoice date.procedure1.0
IndicatorFires whenFamilyWeight
new_vendor_large_payment@2A large payment, against payments in the same currency, to a vendor created in the last 30 days.vendor_profile1.4

release_payment

Release of a payment. Required data fields: actor, at, on, outcome.

AssertionRequiresDecided byRisk weight
releaser_authorizedThe releaser's authority covers the amount.procedure1.5
releaser_independentThe releaser neither approved the invoice nor created the vendor.procedure2.0
bank_change_settledNo unverified bank change in the 14 days before release.procedure2.5

Judgment questions and qualification

QuestionAsksStatusHoldout error bounds
business_expense@1Is this a business expense rather than a personal one?qualified in the published resultsholds 0.00232
exception_justified@2Does the recorded rationale justify an exception to this policy rule?qualified in the published resultsholds 0.00488
expense_category@2Which category best fits this expense?qualified in the published resultsholds 0.00243
invoice_specific@1Does this invoice describe specific goods or services?qualified in the published resultsfails 0.00468, holds 0.00331

The table describes published evaluation evidence. For the qualification attached to the running service, inspect this use case’s live definition. A model answer can be recorded without deciding a check. How qualification works.

Scope and limits

  • The purchase-order check tests whether a reference is present above the configured threshold. It does not authenticate a purchase order, match its lines to the invoice or verify receipt of the goods.
  • Correct invoice arithmetic establishes that the submitted lines, tax and discount reconcile. It does not independently establish occurrence, completeness or the authenticity of supporting documents.
  • Receipt status, vendor status and bank-change verification are supplied fields. An account match or a recorded callback does not authenticate the supplier's payment instruction or establish the independent origin of the contact used to verify it.
  • The vendor-status check uses the supplied master status, defaulting to active if omitted, and compares a creation date when present. It does not reconstruct historical status changes.
  • Materiality remains the reviewer's planning and evaluation judgment. The current API does not accept materiality as an engagement field, and a small amount does not automatically waive an assertion.
  • The checks cover the registered transaction and decision types below. They do not produce a financial-statement opinion, tax conclusion or a complete assessment of internal control.

A true verdict means all applicable, unskipped checks hold within the decision gates; false means at least one qualifying failure decided the item. Other assertions can remain unresolved even with a false verdict. An uncertain verdict means the evidence and decision gates do not support a decided result. Review the assertion outcomes and evidence before taking a business action. Read the decision method.

Published evaluation results

These measurements use synthetic populations with generator-stated truth. Coverage is the share receiving true or false verdicts, not measured accuracy on your records. Read the metrics and their limits.

Results for financial-audit@4.

financial-audit@4 is measured on its own holdout dataset (0043b0a536a0bc1c…), not the one financial-audit@1, financial-audit@2 and financial-audit@3 were measured on, so its numbers are not directly comparable with theirs.

ConfigurationItemsDecided coverageCoverage gain (pp)Uncertain per 1,000True, rules: errors / nTrue, with modelFalse, rulesFalse, with model
Rules only810051.4%0.0 pp486.40 / 1932, ≤ 0.15%–0 / 2228, ≤ 0.13%–
With smalldecide 00607d88f6e1:float32-cpu810087.2%35.9 pp127.70 / 3203, ≤ 0.09%0 / 1578, ≤ 0.19%0 / 2253, ≤ 0.13%0 / 32, ≤ 8.94%

Faults

FaultItemsFalse verdictsFalse or uncertain
approval_after_payment30100.0%100.0%
approval_by_claimant_user30100.0%100.0%
approved_faulty_claim336100.0%100.0%
approved_faulty_invoice215100.0%100.0%
approver_without_authority30100.0%100.0%
arithmetic_error56100.0%100.0%
category_code_mismatch55100.0%100.0%
claim_after_termination52100.0%100.0%
claim_before_hire55100.0%100.0%
claim_outside_period52100.0%100.0%
creator_also_pays50100.0%100.0%
creator_without_role30100.0%100.0%
duplicate_claim103100.0%100.0%
duplicate_invoice_amount_date108100.0%100.0%
duplicate_invoice_number96100.0%100.0%
duplicate_invoice_typo112100.0%100.0%
duplicate_payment60100.0%100.0%
inactive_vendor_invoice53100.0%100.0%
incomplete_vendor30100.0%100.0%
invoice_approver_without_authority30100.0%100.0%
invoice_entered_and_approved30100.0%100.0%
miscategorized_claim890.0%100.0%
missing_po74100.0%100.0%
missing_receipt82100.0%100.0%
near_duplicate_claim110100.0%100.0%
over_balance_payment30100.0%100.0%
over_limit85100.0%100.0%
payment_before_invoice30100.0%100.0%
payment_before_verification60100.0%100.0%
payment_to_employee_account30100.0%100.0%
payment_to_other_account30100.0%100.0%
personal_expense910.0%100.0%
release_by_invoice_approver30100.0%100.0%
release_unverified_bank_change30100.0%100.0%
releaser_without_authority30100.0%100.0%
self_approval30100.0%100.0%
self_verified_bank_change30100.0%100.0%
shared_receipt108100.0%100.0%
split_claim123100.0%100.0%
subordinate_approval30100.0%100.0%
unjustified_exception400.0%100.0%
unmatched_payment30100.0%100.0%
unverified_bank_change30100.0%100.0%
vague_invoice690.0%100.0%
vendor_employee_address30100.0%100.0%
vendor_employee_bank134100.0%100.0%

Look-alikes

Look-alikeItemsFalse verdictsUncertain
justified_exception400.0%100.0%
new_vendor_large_payment100.0%0.0%
recurring_invoice550.0%100.0%
repeat_purchase600.0%100.0%
round_amount550.0%100.0%
same_merchant_under_limit600.0%100.0%
team_dinner600.0%100.0%
weekend_travel500.0%100.0%

Holdout seeds 101, 102, 103, 104, 105, 106, 107, 108, 109, 110; dataset SHA-256 0043b0a536a0bc1c….

Run identity and dataset
ConfigurationRecorded atSource commitModel artifact SHA-256Dtype
deterministic_only2026-10-07T05:00:29+00:00a35faac58138ed25537ebf9d6ee8f7eb215926d3nonenone
smalldecide:00607d88f6e1:float32-cpu2026-10-07T06:08:26+00:00a35faac58138ed25537ebf9d6ee8f7eb215926d300607d88f6e152b471e557f6a814471ab5a6faf900cae427d8b3d50d4614be17float32-cpu

Dataset SHA-256: 0043b0a536a0bc1c880de974087a20182e1c9620ca62fc19f000677b42bdb507.

Versions

A request naming financial-audit runs the current version. Name a version, such as financial-audit@1, to keep the checks you integrated against; a superseded version stays served unchanged. Test labels in a response carry the version that ran them.

VersionWhat it checks differently
financial-audit@4The current version, used when a request names financial-audit alone. It is version 3 with one change: an approval that records a policy exception asks exception_justified@2 instead of exception_justified@1. Version 1 of that question tells the model how far the claim exceeds its limit even when the limit is in another currency; version 2 states the excess only against a limit in the claim's currency, and otherwise names the limit's currency and states no excess. Its question and criteria are unchanged, and on a limit in the claim's currency its input is the same. Like every question version it carries its own qualification and decides only when qualified for the running model; without one, an approval of a claim or invoice that verifies false, with a recorded exception, is at most uncertain (a failing deterministic check still makes it false). Its published results come from its own holdout dataset, which adds approvals of claims whose limit is in another currency.
financial-audit@3Superseded, and still served unchanged to requests that name financial-audit@3. Risk indicators compare amounts only within one currency, since no exchange rate is assumed: near_limit and near_limit_habit fire only against a limit in the claim's currency; the category, vendor and new-vendor payment outliers and the vendor's first-digit test compare a record only with records in its own currency; and round_amount reads round_units and round_minimums (invoices: invoice_round_units and invoice_round_minimums), amounts keyed by currency that default to version 2's USD values, so a currency they do not list fires no round-amount indicator; each pair must list the same currencies. Changed indicators carry a new version, such as round_amount@2. Two tests no longer read a missing field as an answer: approved_before_payment needs the claim's paid_on, a date or null when the claim has not been paid, and is insufficient_evidence when it is absent. paid_on null asserts the claim is unpaid; if your export writes null for an unknown payment date, omit the field instead; not_shared without a merchant is not applicable only to per diem and mileage claims, and insufficient_evidence for any other claim. On single-currency records that state paid_on as a date and name a merchant, version 3 reaches version 2's outcomes and risk points. Its exception_justified@1 question is told the excess over a limit even when the limit is in another currency.
financial-audit@2Superseded, and still served unchanged to requests that name financial-audit@2. Receipt and PO thresholds are set per currency: receipt_thresholds and po_thresholds map a three-letter currency code to an amount, defaulting to 75.00 and 1000.00 USD. A claim or invoice in a listed currency is judged against that amount exactly as version 1 judged one in its threshold currency. One in any other currency holds only when the receipt or PO reference is present, and is otherwise insufficient_evidence; no exchange rate is assumed. A map you send replaces the default, so list every currency you need. Its risk indicators compare amounts without regard to currency, with one round_unit and round_minimum. A missing paid_on is not applicable; a claim with no merchant is not applicable when the expense population is supplied, and insufficient_evidence without it.
financial-audit@1Superseded, and still served unchanged to requests that name financial-audit@1. One receipt_threshold and one po_threshold in threshold_currency (default USD); a record in another currency holds only with its receipt or PO reference.

Schemas and integration

Every item, record and reference schema is at GET /api/v1/use-cases/financial-audit.

Supported context record types: employee, expense, approve_expense, vendor, create_vendor, change_vendor_bank, invoice, approve_invoice, payment, release_payment.

Supported reference sets: approval_limit, approval_authority, user_role, vendor_bank_change, calendar, personal_terms.

Start with a reviewable sample

Begin with a defined transaction population and control objective. Include known exceptions, complete records and cases with missing evidence; compare the findings with your audit workpapers before expanding the workflow.

Try the console · API quickstart · Full API reference

From the blog