Classify card disputes and detect friendly fraud
Grayson classifies card disputes as friendly fraud, account takeover or stolen card from authentication, logins and past disputes, and recommends a resolution.
Grayson checks a cardholder's unauthorized-transaction claim against how the transaction was authenticated, the logins and devices around it, and the cardholder's merchant and dispute history. It returns how likely the cardholder authorized the transaction, whether it looks like first-party (friendly) fraud, account takeover, a stolen card or an authorized purchase, and whether to approve, give provisional credit or deny, for about $0.06 per 1,000 decisions.
- Decides: Classify unauthorized-transaction claims and choose provisional credit, approval or denial.
- Call it: When a cardholder files an unauthorized-transaction claim
- Questions: 2 choice, 1 score, 1 yes/no
- Cost: $0.000060 per decision, $0.06 per 1,000, for this example's 1,688 input tokens
- Latency: 219 ms for this example, the median of 5 calls through api.finic.ai from US-West
Example
A member disputes a $1,149 online electronics purchase that passed a one-time code sent to their phone, came from their home IP address and shipped to their home, though their card number was exposed in an unrelated breach about two weeks earlier.
| Question | Grayson's answer |
|---|---|
claim_type | first_party, 73% |
authorized_likelihood | Very likely (over 90%), 71% |
records_contradict_claim | Yes, P(yes) 97% |
resolution | deny, 93% |
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.
{
"model": "grayson-1",
"context": {
"member": {
"member_id": "mem_20417",
"member_since": "2018-05-22",
"home_address_on_file": "1427 Alder Ct, Dayton, OH",
"phone_on_file": "x0148, unchanged since 2022-03-09",
"email_on_file": "unchanged since 2021-11-02",
"products": [
"Checking x3307",
"Savings x3315",
"Visa debit card x4821"
]
},
"card": {
"card": "Visa debit card x4821",
"issued": "2025-07-14",
"status": "Active",
"mobile_wallet": "Provisioned to the member's iPhone on 2025-07-16",
"network_alerts": [
{
"date": "2026-08-27",
"type": "Compromised-account alert from the card network",
"detail": "Card number and expiration date exposed in a data breach at an online retailer (not the disputed merchant)"
}
]
},
"spend_profile_12_months": {
"card_transactions": 418,
"typical_amount_usd": "6 to 150",
"largest_before_dispute_usd": 389,
"card_not_present_share_pct": 34,
"top_categories": [
"Grocery",
"Fuel",
"Restaurants",
"Online retail",
"Streaming subscriptions"
],
"countries": [
"US"
]
},
"history_with_disputed_merchant": [
{
"date": "2025-12-06",
"amount_usd": 74.99,
"authentication": "3-D Secure, frictionless",
"ip": "198.51.100.24",
"ship_to": "Home address on file",
"disputed": false
},
{
"date": "2026-04-18",
"amount_usd": 129,
"authentication": "3-D Secure, frictionless",
"ip": "198.51.100.24",
"ship_to": "Home address on file",
"disputed": false
}
],
"prior_disputes": [
{
"filed": "2025-12-29",
"merchant": "Online apparel retailer",
"amount_usd": 212.4,
"reason": "Merchandise not received",
"outcome": "Credited; merchant did not respond to the chargeback"
},
{
"filed": "2026-05-11",
"merchant": "Meal kit subscription",
"amount_usd": 89.99,
"reason": "Recurring charge after cancellation",
"outcome": "Credited; merchant accepted the chargeback"
}
],
"digital_banking": {
"known_devices": [
{
"device": "iPhone, mobile banking app",
"first_seen": "2024-02-11"
},
{
"device": "Windows laptop, Chrome",
"first_seen": "2022-09-03"
}
],
"usual_ip": "198.51.100.24 (home internet provider, Dayton, OH; 214 logins in the last 12 months)",
"profile_changes_last_90_days": "None",
"events": [
{
"ts": "2026-09-12T23:41:05Z",
"type": "login",
"device": "iPhone, mobile banking app",
"ip": "198.51.100.24"
},
{
"ts": "2026-09-13T00:19:44Z",
"type": "login",
"device": "iPhone, mobile banking app",
"ip": "198.51.100.24"
},
{
"ts": "2026-09-16T12:03:10Z",
"type": "login",
"device": "iPhone, mobile banking app",
"ip": "198.51.100.24"
},
{
"ts": "2026-09-22T22:47:31Z",
"type": "login",
"device": "Windows laptop, Chrome",
"ip": "198.51.100.24"
},
{
"ts": "2026-09-30T13:15:00Z",
"type": "statement_available",
"detail": "September statement, which lists the disputed purchase"
},
{
"ts": "2026-10-01T14:58:12Z",
"type": "login",
"device": "iPhone, mobile banking app",
"ip": "198.51.100.24"
}
]
},
"disputed_transaction": {
"authorized": "2026-09-13T00:02:17Z",
"posted": "2026-09-14",
"merchant": "Online electronics retailer",
"merchant_category": "5732 Electronics stores",
"merchant_country": "US",
"amount_usd": 1149,
"channel": "Card not present, e-commerce",
"card_details": "Card number keyed at checkout (not the mobile wallet)",
"avs_result": "Full match",
"cvv2_result": "Match",
"three_d_secure": {
"result": "Authenticated after a challenge (ECI 05)",
"challenge": "One-time code sent by SMS to the phone on file (x0148) at 2026-09-13T00:01:02Z; the correct code was entered at 00:01:49Z on the first attempt",
"device": "iPhone, mobile Safari",
"ip": "198.51.100.24",
"ship_to": "Home address on file",
"bill_to": "Home address on file"
}
},
"claim": {
"filed": "2026-10-01T15:22:00Z",
"channel": "Phone",
"reason_selected": "Unauthorized transaction",
"amount_usd": 1149,
"card_in_possession": true,
"card_reported_lost_or_stolen": false,
"cardholder_statement": "I did not make this purchase. I've never shopped at that store and I don't know what was bought. My card has been in my wallet the whole time and nobody else uses it. That charge took most of my rent money and I need it back.",
"agent_notes": "Member says no one else has access to the card or phone. No suspicious calls or texts, never shared a code with anyone. Asked about the verification text on 9/12: member doesn't remember getting one. Merchant evidence not yet requested."
}
},
"questions": {
"claim_type": {
"type": "choice",
"instructions": "Which explanation best fits the transaction or transactions the cardholder is disputing?",
"options": {
"first_party": "First-party (friendly) fraud: the cardholder made or agreed to the transaction and is knowingly claiming they didn't",
"account_takeover": "Account takeover: someone else got into the cardholder's online banking, phone, email or mobile wallet and used the card through that access",
"stolen_card": "Third-party fraud with a lost, stolen or counterfeit card or stolen card details, without access to the cardholder's other accounts",
"authorized_or_merchant": "Not fraud: the transaction was authorized, and the cardholder doesn't recognize it or has a problem with the merchant (an unfamiliar billing name, a purchase by someone they let use the card, a subscription, goods not received)"
}
},
"authorized_likelihood": {
"type": "score",
"instructions": "How likely is it that the cardholder made the disputed transaction or transactions, or let someone else make them with their card?",
"levels": [
"Very unlikely (under 10%)",
"Unlikely (10-40%)",
"Uncertain (40-60%)",
"Likely (60-90%)",
"Very likely (over 90%)"
]
},
"records_contradict_claim": {
"type": "noul",
"instructions": "Do the institution's own records contradict what the cardholder says in the claim?"
},
"resolution": {
"type": "choice",
"instructions": "How should the institution resolve this unauthorized-transaction claim at intake?",
"options": {
"approve": "Approve the claim now: credit the amount permanently and close it as unauthorized",
"provisional_credit": "Give provisional credit and investigate further before deciding",
"deny": "Deny the claim and send the cardholder a written explanation with the evidence that the transaction was authorized"
}
}
}
}Probabilities are shortened to four decimals here; responses carry full precision.
{
"id": "dec_1dcc1d5103c44e9e89f88ffa966e7896",
"model": "grayson-1",
"answers": {
"claim_type": {
"type": "choice",
"value": "first_party",
"probabilities": {
"first_party": 0.7318,
"account_takeover": 0.0172,
"stolen_card": 0.0413,
"authorized_or_merchant": 0.2097
}
},
"authorized_likelihood": {
"type": "score",
"value": 3.425,
"level": "Very likely (over 90%)",
"probabilities": [
0.0452,
0.0581,
0.0311,
0.1579,
0.7077
]
},
"records_contradict_claim": {
"type": "noul",
"value": true,
"probability": 0.9707
},
"resolution": {
"type": "choice",
"value": "deny",
"probabilities": {
"approve": 0.036,
"provisional_credit": 0.036,
"deny": 0.928
}
}
},
"usage": {
"input_tokens": 1688
}
}claim_typeroutes the case: first-party and authorized purchases to an investigator, account takeover to account security, stolen cards to chargeback and reissue.authorized_likelihood: add "Likely" and "Very likely"; approve below a low cutoff, and have an investigator confirm a denial above a high one.resolution: approve automatically only when the approve probability is high and the records don't contradict the claim; have a person review every denial.
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.jsonThe problem
An unauthorized-transaction claim can be third-party fraud, or first-party fraud by a cardholder who made the purchase and disputes it anyway. Rules handle the simple cases, such as chip and PIN with the card in the cardholder's hand, but miss the ones where the claim and the records disagree, such as a correct one-time code entered from the cardholder's home internet connection.
What to send
Send the claim with the records an investigator would pull:
- How the transaction was authenticated. Chip and PIN or a 3-D Secure code narrows who could have made it.
- Device, IP and shipping address from checkout. A match with the cardholder's banking sessions or home points to their household.
- Logins and profile changes around the transaction. A reset, new device or contact change just before points to account takeover.
- Merchant and dispute history. Undisputed earlier purchases, or repeated claims at merchants they keep using, point to the cardholder.
- Compromise and testing signals. A breach alert or small test charge makes third-party fraud plausible, not proven.
- The claim in the cardholder's words. Grayson reads it against the records; contradictions are what investigators look for.
Add your own criteria
Institutions draw the line for denying a claim at intake differently, and examiners expect you to follow your own written procedure, so put it in the request. This one allows a denial at intake only with strong authentication, confirmed delivery to the address on file, and no compromised-account alert on the card in the 180 days before the transaction.
Your intake denial policy adds this to the context:
{
"disputes_procedure": "Debit card unauthorized-transaction claims, intake (procedure DC-7, rev. 2026-06). An intake analyst may deny a claim without provisional credit only when all three of these hold: (1) the transaction was authenticated with something only the member controls, meaning chip and PIN with the card in the member's possession, or a 3-D Secure challenge sent to a phone number unchanged for at least 30 days; (2) for goods shipped to an address, we hold the merchant's carrier delivery confirmation to the member's address on file; (3) the card has not appeared in a card network compromised-account alert in the 180 days before the transaction. If any of the three is not met, give provisional credit within 10 business days of the claim, request the merchant's evidence, and assign the case to an investigator, who makes the final decision. Never approve at intake a claim that our records contradict."
}| Question | Without | With your intake denial policy |
|---|---|---|
claim_type | first_party, 73% | first_party, 69% |
authorized_likelihood | Very likely (over 90%), 71% | Very likely (over 90%), 33% |
records_contradict_claim | Yes, P(yes) 97% | Yes, P(yes) 85% |
resolution | deny, 93% | provisional_credit, 94% |
Only the authentication condition holds (there's no delivery confirmation yet, and the card was in a compromised-account alert 17 days before the purchase), so the policy calls for provisional credit and an investigator's decision instead of a denial at intake.
Where to call it
- At intake, while the cardholder is still on the phone, so the agent can see the answers and ask follow-up questions.
- Send uncertain cases to an analyst with the answers attached, and give provisional credit if the investigation will run past 10 business days.
- Call it again when the merchant's evidence arrives, with that evidence added to the context, before the final decision.
Cost and latency
This example is 1,688 input tokens, so a decision costs $0.000060: $0.06 per 1,000 decisions, or $60.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 219 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/card-dispute-classification/questions.json --label claim_type=<column> --label authorized_likelihood=<column> --label records_contradict_claim=<column> --label resolution=<column>Each --label names the column with that question's right answer:
claim_type:first_party,account_takeover,stolen_card,authorized_or_merchantauthorized_likelihood: a level, such as "Very likely (over 90%)"records_contradict_claim:trueorfalseresolution:approve,provisional_credit,deny
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
Can Grayson tell friendly fraud from a genuine fraud claim?
It weighs the evidence an investigator uses: whether the authentication relied on something only the cardholder controls, whether the device and IP match their banking sessions, whether they've bought from the merchant before, and whether their statement fits those records. When the records conflict, as when a card number was exposed in a breach but the purchase passed a code sent to the cardholder's phone, the probabilities spread out, and that's the case to send to a person.
Does this replace a Regulation E investigation?
No. Regulation E requires a reasonable investigation of each claim and, when you find no error, a written explanation and the cardholder's right to the documents you relied on. Use Grayson to triage at intake, point investigators at the evidence and keep decisions consistent, and keep a person responsible for denials.
Does it work for credit card disputes?
Yes. The classification is the same, but credit card claims fall under Regulation Z, which caps the cardholder's liability for unauthorized use at $50 and lets them withhold payment of the disputed amount while you investigate, instead of requiring provisional credit. Change the resolution options to match your credit card process, or use network reason codes as choice options with descriptions.
Related recipes
Screen card authorizations for fraud in real time
Score card authorizations for fraud and choose approve, step up or decline, next to your existing rules.
Fraud typology classification
Label a fraud claim with a FraudClassifier class, scam type and contributing factors; decide reimbursement.
Recipes
Every use case, with its questions and cost per decision.
Card authorizations
Grayson screens card authorizations for card testing, fallback and card-not-present fraud from recent card activity, and picks approve, step up or decline.
Check deposits
Grayson decides whether a deposited check is counterfeit, altered, stolen or a duplicate, and whether to release funds, place an exception hold or reject it.