Detect business email compromise in vendor payments

Decide whether a change to a vendor's bank details is business email compromise, and whether to release, hold or block the payment, from AP and login records.

Grayson decides whether a change to a vendor's bank details is business email compromise (BEC), whether the new account has really been confirmed with the vendor, and whether the next payment should be released, held for a call-back or blocked. It reads the vendor's payment history, the change request, each verification step and the sessions of the users involved, and costs about $0.05 per 1,000 decisions.

  • Decides: Catch payments to a vendor's new bank account after a fake or compromised change request.
  • Call it: Before a payment goes to a vendor's recently changed bank details
  • Questions: 1 score, 1 yes/no, 1 choice
  • Cost: $0.000047 per decision, $0.05 per 1,000, for this example's 1,319 input tokens
  • Latency: 184 ms for this example, the median of 5 calls through api.finic.ai from US-West

Example

A veterinary group changed a long-standing supplier's bank details after an email from the supplier's real address, confirmed them by calling the number given in that email, and has just approved a $46,850 payment to the new account.

Open in PlaygroundEdit and run this request in the Finic portal.
QuestionGrayson's answer
bec_likelihoodLikely (60-90%), 25%
verified_out_of_bandNo, P(yes) under 1%
actionhold_and_verify, 81%

Each percentage is Grayson's probability for the answer shown; for a yes/no question it's the probability of yes. A multiple-choice answer lists the options at 50% or more.

  • bec_likelihood: add the "Likely" and "Very likely" probabilities; above your threshold, the payment shouldn't go out on the customer's checklist alone.
  • verified_out_of_band: a low probability means the change isn't really confirmed, whatever the attestation says, so send the payment to a call-back queue.
  • action: when release isn't clearly the most likely option, hold rather than release; a held payment can still go out on the due date.

Call it from your code

Save request.json and send it with your API key in GRAYSON_API_KEY:

curl https://api.finic.ai/v1/decide \
  -H "Authorization: Bearer $GRAYSON_API_KEY" \
  -H "Content-Type: application/json" \
  --data @request.json

The problem

Vendors do change banks, the invoice is real, the new account passes validation and the micro-deposits confirm, so every step of a BEC payment can look normal. A rule that holds every bank change flags changes that are almost all legitimate, and one that trusts a completed checklist misses a call-back placed to the number in the fraudulent email.

What to send

Send the vendor record, the change request as it arrived, each verification step and the sessions of the users involved:

  • How the change was requested. A lookalike domain registered days earlier is close to conclusive; the vendor's real domain proves little.
  • Timing and urgency. A change shortly before a large invoice is due, with pressure to update, fits the pattern.
  • The new account. An owner name no-match is a strong signal; an account that is merely valid tells you little.
  • How it was verified. A call-back counts only to a number on file before the request.
  • The vendor's history. A first-ever bank change on a long relationship deserves more care.
  • The users' sessions. A new device, a new country or repeated MFA prompts point to a taken-over login, not a fooled clerk.

Add your own criteria

Teams differ on what a failed check means: some call the vendor after a name mismatch, while others treat an emailed change with a mismatch as fraud and cancel the payment outright. This policy takes the stricter line and spells out what counts as verification.

Your vendor bank-change policy adds this to the context:

{
  "platform_policy": "AP-7, vendor bank-detail changes. A change counts as verified only after a call to a phone number that was on file before the request arrived; a number, name or link supplied in the request never counts, and neither do micro-deposits. If a change was requested by email and the account validation owner name check returns anything other than a full match, treat it as suspected business email compromise and do not hold the payment for a call-back: cancel the payment, restore the vendor's previous verified bank details, lock vendor edits for the customer, and phone the customer's administrator at the number on file. The customer must re-verify the vendor and submit a new payment."
}
QuestionWithoutWith your vendor bank-change policy
bec_likelihoodLikely (60-90%), 25%Very likely (over 90%), 85%
verified_out_of_bandNo, P(yes) under 1%No, P(yes) 1%
actionhold_and_verify, 81%block_and_alert, 99%

The change was requested by email and the owner name check returned no match, so under this policy the payment should be cancelled and the old bank details restored rather than held for a call-back.

Where to call it

  • When a vendor's bank details change, before the change takes effect, to decide how much verification to require.
  • When a payment to recently changed details is approved, before it is sent.
  • When the answer is uncertain, hold the payment and call the vendor at the number on file.

Cost and latency

This example is 1,319 input tokens, so a decision costs $0.000047: $0.05 per 1,000 decisions, or $47.00 per million. You pay only for input tokens, at $0.035 per million, and each request is rounded up to the next millionth of a dollar. A larger context costs proportionally more; every response reports its size in usage.input_tokens.

Grayson answered this example in 184 ms, the median of 5 calls through api.finic.ai from US-West. Latency grows with the number of input tokens. Add your own network time to api.finic.ai.

Evaluate on your own data

Score Grayson on your own past cases before you use it: a CSV with one row per case and a column with the right answer to each question. Every other column is sent as the case.

pipx install https://docs.finic.ai/downloads/grayson_cli-0.2.2-py3-none-any.whl
grayson eval my-cases.csv --questions https://docs.finic.ai/recipes/business-email-compromise/questions.json --label bec_likelihood=<column> --label verified_out_of_band=<column> --label action=<column>

Each --label names the column with that question's right answer:

  • bec_likelihood: a level, such as "Very likely (over 90%)"
  • verified_out_of_band: true or false
  • action: release, hold_and_verify, block_and_alert

Or run grayson on its own to set up your questions step by step. You get each question's accuracy and a CSV with Grayson's answer next to yours for every case.

FAQ

Do micro-deposits or an account-validation check prove the change is legitimate?

No. A confirmed micro-deposit proves only that whoever supplied the bank details can see the account, which a fraudster can do for an account they opened. An owner name check is stronger, but fraudsters open accounts under names close to the vendor's, so send these results as evidence together with how the vendor was actually contacted.

What if the request came from the vendor's real email address?

Then domain and email authentication checks pass, because a compromised vendor mailbox sends from the real domain, often inside a genuine invoice thread. The other signals carry the decision: the new bank, the owner name check, a phone number that appears only in the request, urgency before a large payment, and whether other customers of the vendor received the same change. Grayson weighs these together, so you don't need a separate rule for each.

Should I score the bank-detail change or the payment?

Both. At change time, ask whether the change is fraudulent and whether it has been verified, to decide what verification to require; at payment time, add the invoice, the amount and the approvers' sessions and ask the action question too, since money is about to move. Keep the wording of shared questions identical so thresholds carry over.

On this page