Screen card authorizations for fraud in real time
Grayson screens card authorizations for card testing, fallback and card-not-present fraud from recent card activity, and picks approve, step up or decline.
Grayson screens a card authorization against the card's recent activity and the cardholder's usual spending, and returns how likely it is to be fraud, whether the card number was just run through card testing, and whether to approve, step up or decline. It can run in parallel with your existing rules and models as an extra signal, and costs about $0.04 per 1,000 decisions.
- Decides: Score card authorizations for fraud and choose approve, step up or decline, next to your existing rules.
- Call it: When a card authorization request arrives
- Questions: 1 score, 1 yes/no, 1 choice
- Cost: $0.000041 per decision, $0.04 per 1,000, for this example's 1,165 input tokens
- Latency: 168 ms for this example, the median of 5 calls through api.finic.ai from US-West
Example
A credit card number passed a $1 test at a charity donation page under a BIN attack, and 21 minutes later shows up in a keyed $749.99 online electronics purchase with no 3-D Secure, no security code and an address mismatch.
| Question | Grayson's answer |
|---|---|
fraud_likelihood | Very likely (over 90%), 82% |
card_tested | Yes, P(yes) 98% |
action | decline, 82% |
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": {
"cardholder": {
"account_id": "acct_77310",
"product": "Visa rewards credit card x7193",
"account_open_since": "2019-04-11",
"home_city": "Columbus, OH",
"credit_limit_usd": 12000,
"available_credit_usd": 9340.18,
"phone_on_file_last_changed": "2023-01-15",
"mobile_wallet": "Card provisioned to the cardholder's iPhone on 2024-05-02",
"alerts": "Enrolled in two-way text alerts"
},
"spend_profile_12_months": {
"authorizations": 612,
"card_present_share_pct": 71,
"typical_amount_usd": "8 to 140",
"largest_amount_usd": 486.2,
"countries": [
"US"
],
"card_not_present_merchants": [
"Streaming subscription",
"Online marketplace",
"Food delivery app",
"Airline"
],
"electronics_purchases": "2 in 12 months, both in store with the mobile wallet: $54.10 and $129.99"
},
"recent_authorizations": [
{
"ts": "2026-10-03T13:12:44Z",
"merchant": "Grocery store",
"merchant_category": "5411 Grocery stores",
"location": "Columbus, OH",
"amount_usd": 86.42,
"entry": "Contactless, mobile wallet",
"result": "Approved"
},
{
"ts": "2026-10-03T17:55:10Z",
"merchant": "Fuel station",
"merchant_category": "5542 Automated fuel dispensers",
"location": "Columbus, OH",
"amount_usd": 48.3,
"entry": "Chip",
"result": "Approved"
},
{
"ts": "2026-10-03T21:47:09Z",
"merchant": "Online donation page for a small charity",
"merchant_category": "8398 Charitable and social service organizations",
"location": "US, e-commerce",
"amount_usd": 1,
"entry": "Card number keyed",
"cvv2": "Not provided",
"avs": "Not sent",
"result": "Declined: wrong expiration date"
},
{
"ts": "2026-10-03T21:47:16Z",
"merchant": "Online donation page for a small charity",
"merchant_category": "8398 Charitable and social service organizations",
"location": "US, e-commerce",
"amount_usd": 1,
"entry": "Card number keyed",
"cvv2": "Not provided",
"avs": "Not sent",
"result": "Declined: wrong expiration date"
},
{
"ts": "2026-10-03T21:47:22Z",
"merchant": "Online donation page for a small charity",
"merchant_category": "8398 Charitable and social service organizations",
"location": "US, e-commerce",
"amount_usd": 1,
"entry": "Card number keyed",
"cvv2": "Not provided",
"avs": "Not sent",
"result": "Approved"
}
],
"donation_page_activity_on_our_cards": {
"window": "2026-10-03T21:00:00Z to 2026-10-03T22:08:00Z",
"authorization_attempts": 1840,
"distinct_card_numbers": 1712,
"declined_pct": 94,
"top_decline_reasons": [
"Invalid card number",
"Wrong expiration date"
],
"pattern": "Card numbers on this card's BIN, in runs that differ only in the last 4 to 6 digits; $1.00 attempts a few seconds apart",
"usual_volume": "About 3 authorizations a day on our cards"
},
"authorization_under_review": {
"ts": "2026-10-03T22:08:31Z",
"merchant": "Online electronics retailer",
"merchant_category": "5732 Electronics stores",
"merchant_country": "US",
"amount_usd": 749.99,
"channel": "E-commerce",
"entry": "Card number keyed (not a stored credential or wallet token)",
"eci": "07 (e-commerce, not authenticated with 3-D Secure)",
"avs": "No match (street address and ZIP code)",
"cvv2": "Not provided",
"first_purchase_at_merchant": true,
"network_risk_score": "41 on a 1 to 99 scale (higher is riskier)",
"rules_engine": "No rule fired"
}
},
"questions": {
"fraud_likelihood": {
"type": "score",
"instructions": "How likely is it that the authorization under review is fraudulent, meaning the cardholder didn't make it?",
"levels": [
"Very unlikely (under 10%)",
"Unlikely (10-40%)",
"Uncertain (40-60%)",
"Likely (60-90%)",
"Very likely (over 90%)"
]
},
"card_tested": {
"type": "noul",
"instructions": "Was this card number checked by card testing before the authorization under review? Card testing means small or zero-amount authorizations, often at an unrelated merchant and often with failed attempts, used to confirm that a stolen or generated card number works."
},
"action": {
"type": "choice",
"instructions": "What should happen to the authorization under review?",
"options": {
"approve": "Approve the authorization",
"step_up": "Step up: confirm with the cardholder first (a 3-D Secure challenge, or a one-time code or text alert to the phone on file) and approve only if they confirm",
"decline": "Decline the authorization and block the card for reissue"
}
}
}
}Probabilities are shortened to four decimals here; responses carry full precision.
{
"id": "dec_6248f901211b490688921ccb283e2700",
"model": "grayson-1",
"answers": {
"fraud_likelihood": {
"type": "score",
"value": 3.618,
"level": "Very likely (over 90%)",
"probabilities": [
0.036,
0.036,
0.0218,
0.0864,
0.8197
]
},
"card_tested": {
"type": "noul",
"value": true,
"probability": 0.9841
},
"action": {
"type": "choice",
"value": "decline",
"probabilities": {
"approve": 0.0193,
"step_up": 0.1613,
"decline": 0.8194
}
}
},
"usage": {
"input_tokens": 1165
}
}actionis the recommendation. Start by combining it with your existing decision, for example stepping up when your rules approve but Grayson favors decline.fraud_likelihood: threshold the sum of "Likely" and "Very likely", with separate cutoffs for card-present, card-not-present and cross-border authorizations.card_testedis about the card: a number that passed a test is compromised even if this purchase is declined, so block and reissue it.
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
Rules check fixed conditions, so they miss a $749.99 purchase that follows failed $1 tests at a charity page when no single threshold is crossed, and they decline a traveling cardholder's first foreign purchase. The difference is in the context: what the card did in the last hour, activity at the same merchant across your cards, and how the cardholder usually pays.
What to send
Send the authorization with the card's recent activity and a summary of the cardholder's usual spending:
- Recent authorizations, including declines. $0 or $1 tests minutes before a larger purchase are card testing.
- Activity at the same merchant across your cards. A burst of declines on sequential numbers from one BIN is a BIN attack.
- How the card was read. A fallback read on a chip card that has never needed one is a classic counterfeit signal.
- Card-not-present checks. 3-D Secure authentication, especially after a challenge, is strong evidence of the cardholder.
- Usual pattern and recent locations. These separate impossible travel from a cardholder who is simply traveling.
- Your existing scores and rule results. Grayson weighs them with the rest of the context.
Add your own criteria
Whether to decline or step up trades fraud losses against false declines, so put your strategy in the request and Grayson applies it. This one gives cardholders enrolled in two-way text alerts a confirmation text instead of a hard decline on card-not-present authorizations under $1,000.
Your step-up policy adds this to the context:
{
"authorization_strategy": "Card-not-present authorization strategy (rev. 2026-08). 1) Cardholders enrolled in two-way text alerts are never hard-declined on a card-not-present authorization under $1,000 for suspected fraud. Step them up instead: return a decline that asks the cardholder to contact us, and text them at the phone on file. If they reply YES within 15 minutes, approve the merchant's retry. If they reply NO or don't reply, block the card and reissue it. 2) Hard-decline and block without a text only when the amount is $1,000 or more, the card is already blocked, or the cardholder has already reported the card lost or stolen."
}| Question | Without | With your step-up policy |
|---|---|---|
fraud_likelihood | Very likely (over 90%), 82% | Very likely (over 90%), 78% |
card_tested | Yes, P(yes) 98% | Yes, P(yes) 99% |
action | decline, 82% | step_up, 93% |
This $749.99 card-not-present authorization belongs to an enrolled cardholder, so the action should move from decline to step-up, while the fraud likelihood and card testing answers stay the same.
Where to call it
- In parallel, with a timeout: run it alongside your rules and model, and fall back to your existing decision if the answer isn't back.
- Where it adds the most: card-not-present, fallback, cross-border and first-time-merchant authorizations, or your rules' borderline cases.
- At 3-D Secure authentication, if you control the risk decision there: a likely-fraud answer becomes a challenge.
Cost and latency
This example is 1,165 input tokens, so a decision costs $0.000041: $0.04 per 1,000 decisions, or $41.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 168 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-authorization-fraud/questions.json --label fraud_likelihood=<column> --label card_tested=<column> --label action=<column>Each --label names the column with that question's right answer:
fraud_likelihood: a level, such as "Very likely (over 90%)"card_tested:trueorfalseaction:approve,step_up,decline
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
Is Grayson fast enough for authorization?
Call Grayson in parallel with your existing checks so its time overlaps with theirs, set a timeout that fits your authorization budget, and use your existing decision when the answer doesn't arrive in time. If the budget is too tight to screen every authorization, call it where it adds the most, such as card-not-present and fallback authorizations, or at 3-D Secure authentication.
Can it detect card testing across many cards, not just one?
Grayson reads one context per request, so compute merchant-level counts in your stream processing (attempts, distinct card numbers, decline rate and reasons, numbers running in sequence on your BIN) and add them to each authorization's context, as in the example. You can also send a merchant's recent authorizations on your cards as the context and ask directly whether the merchant is being used for card testing.
Do AVS and CVV2 matches mean the cardholder is making the purchase?
No. Card details stolen through phishing and checkout-page skimmers usually include the billing address and security code, so these checks tell you most when they fail. 3-D Secure authentication with a challenge to the cardholder's phone or banking app is much stronger evidence, which is why the step-up option uses it.
Related recipes
Classify card disputes and detect friendly fraud
Classify unauthorized-transaction claims and choose provisional credit, approval or denial.
Triage dark web credential and card alerts
Match a dark web alert to the customer, judge whether the exposure still works, and choose the response.
Recipes
Every use case, with its questions and cost per decision.
Business email compromise
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.
Card disputes
Grayson classifies card disputes as friendly fraud, account takeover or stolen card from authentication, logins and past disputes, and recommends a resolution.