Skip to main content
Early Fraud Warnings (EFWs) are real-time alerts issued by card networks when a cardholder reports potential fraud on a transaction. They arrive before a formal dispute (chargeback) is filed, giving you a window to proactively refund a suspicious charge and potentially avoid a chargeback entirely.

Table of Contents


Overview

Card issuer flags transaction as potentially fraudulent


Card network fires radar.early_fraud_warning.created


Flex receives event → resolves charge → creates EFW record


Partner receives webhook event: radar.early_fraud_warning.created

       ├─── actionable: true  → Consider proactive refund to prevent chargeback
       └─── actionable: false → Warning is informational only (already refunded or disputed)
There is no REST API to list or retrieve EFWs. All EFW data is delivered exclusively via webhook events. Subscribe to radar.early_fraud_warning.created and radar.early_fraud_warning.updated in your webhook configuration.

Key facts

  • EFWs are read-only — Flex does not expose an API to query or acknowledge them.
  • An EFW does not mean a dispute has been filed. It is a signal that the cardholder may have flagged the transaction.
  • A single charge may receive multiple EFWs (one created, then updated as circumstances change).
  • actionable: true means the charge has not yet been refunded or disputed — act now.
  • actionable: false means the charge has already been fully refunded or a dispute has been filed. No action needed.

How EFWs Differ from Disputes

Early Fraud WarningDispute (Chargeback)
TriggerCard issuer files fraud report with card networkCardholder formally disputes with their bank
Impact on fundsNo immediate fund withdrawalFunds withdrawn immediately
FeeNo feeDispute fee charged
Flex APIWebhook onlyFull REST API
Recommended actionConsider proactive refundSubmit evidence or accept loss
Time pressureRespond before a formal dispute is filedEvidence deadline (due_by) typically 7–21 days

The EarlyFraudWarning Object

This is the object delivered inside every radar.early_fraud_warning.* webhook event.
{
  "early_fraud_warning_id": "fefw_01jx3q8kt1b9a7n2c4d5e6f7g8",
  "charge_id": "fch_01jx3q6ab1c7d2e3f4g5h6j7k8",
  "payment_intent_id": "fpi_01jx3q7ab1c8d2e3f4g5h6j7k8",
  "actionable": true,
  "fraud_type": "card_never_received",
  "client_reference_id": "order_12345",
  "test_mode": false,
  "created_at": "2026-02-15T10:20:00Z"
}

Field Reference

FieldTypeDescription
early_fraud_warning_idstringFlex EFW ID (fefw_ prefix).
charge_idstring | nullFlex charge ID (fch_) for the flagged transaction.
payment_intent_idstring | nullFlex payment intent ID (fpi_) linked to the charge.
actionablebooleantrue if the charge is still refundable and no dispute has been filed. false once refunded or disputed.
fraud_typestringFraud category from the card issuer. See Fraud Type Reference.
client_reference_idstring | nullThe client_reference_id you passed when creating the checkout session, if any. Useful for correlating EFWs to your internal order IDs.
test_modebooleantrue for test-mode transactions.
created_atstringISO 8601 timestamp when Flex received the EFW.

Fraud Type Reference

ValueDescription
card_never_receivedPhysical card was never received by the cardholder (e.g. intercepted in mail).
fraudulent_card_applicationCard was obtained through a fraudulent application.
made_with_counterfeit_cardPhysical counterfeit card was used.
made_with_lost_cardLost card was used without authorisation.
made_with_stolen_cardStolen card was used without authorisation.
unauthorized_use_of_cardCard was used without the cardholder’s authorisation (general).
miscOther / unclassified fraud type.
unauthorized_use_of_card and card_never_received are the most common in ecommerce.

Webhook Events

radar.early_fraud_warning.created

Fired when Flex receives a new early fraud warning for one of your charges.
{
  "event_id": "fevt_01jx3q8kt1b9a7n2c4d5e6f7g8",
  "event_type": "radar.early_fraud_warning.created",
  "event_dt": 1770000000,
  "object": {
    "early_fraud_warning_id": "fefw_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "charge_id": "fch_01jx3q6ab1c7d2e3f4g5h6j7k8",
    "payment_intent_id": "fpi_01jx3q7ab1c8d2e3f4g5h6j7k8",
    "actionable": true,
    "fraud_type": "card_never_received",
    "client_reference_id": "order_12345",
    "test_mode": false,
    "created_at": "2026-02-15T10:20:00Z"
  }
}

radar.early_fraud_warning.updated

Fired when an existing EFW changes — most commonly when actionable transitions from true to false (because the charge was refunded or a formal dispute was filed).
{
  "event_id": "fevt_01jx3q9ab1c2d3e4f5g6h7j8k9",
  "event_type": "radar.early_fraud_warning.updated",
  "event_dt": 1770003600,
  "object": {
    "early_fraud_warning_id": "fefw_01jx3q8kt1b9a7n2c4d5e6f7g8",
    "charge_id": "fch_01jx3q6ab1c7d2e3f4g5h6j7k8",
    "payment_intent_id": "fpi_01jx3q7ab1c8d2e3f4g5h6j7k8",
    "actionable": false,
    "fraud_type": "card_never_received",
    "client_reference_id": "order_12345",
    "test_mode": false,
    "created_at": "2026-02-15T10:20:00Z"
  }
}

Webhook signature verification

All Flex webhooks include a flex-signature header. Verify it using your webhook signing secret from the Flex dashboard. See the Webhooks documentation for signature verification details.

Idempotency

EFWs are upserted on early_fraud_warning_id — receiving the same event twice will not create duplicate records. Your webhook handler should be idempotent: processing the same event_id twice should have no side-effects.
When you receive radar.early_fraud_warning.created with actionable: true:

1. Assess the risk

Check the fraud_type and correlate the charge_id or client_reference_id to the order in your system.
  • Is this order already shipped? Shipped orders are harder to recover.
  • Is the fraud_type high-risk (card_never_received, made_with_stolen_card)? These have a higher probability of becoming a formal chargeback.

2. Decide: refund or wait

ScenarioRecommendation
Order not yet shippedCancel and refund immediately.
Order shipped, high-value, high-risk fraud typeProactive refund may still be cheaper than losing a dispute + fee.
Order shipped, low-valueWeigh refund cost vs dispute fee (~$15) + chargeback rate impact.
actionable: falseNo action needed — already resolved.

3. Issue a refund (if decided)

Use the Flex Refunds API to issue a full or partial refund for the charge_id in the EFW. Once refunded, the EFW will be updated to actionable: false and you will receive a radar.early_fraud_warning.updated event.

4. Track your chargeback rate

A high chargeback rate (typically >1%) can result in penalties from card networks. EFWs are an early signal to detect patterns — monitor fraud_type across EFWs to identify systemic fraud patterns in your customer base.

End-to-End Testing Guide

Note: EFW events cannot be triggered via test card payments alone — card networks do not issue EFWs in response to standard test transactions. Use the synthetic webhook method below to simulate the full flow end-to-end against your local Flex server.

Prerequisites

  • Flex API key and webhook signing secret (from your Flex dashboard)
  • Your webhook endpoint URL (must be reachable — use ngrok for local development)
  • Python 3 or any HMAC-SHA256 capable language

Step 1 — Register a webhook endpoint

In your Flex dashboard, register your endpoint URL and subscribe to:
  • radar.early_fraud_warning.created
  • radar.early_fraud_warning.updated

Step 2 — Create a charge

Create a checkout session and complete a payment. Note the charge_id (fch_...) and payment_intent_id (fpi_...) from the checkout session response. Optionally include a client_reference_id in your checkout session request to verify it is propagated to EFW events:
POST /v1/checkout/sessions
Authorization: Bearer fsk_...
Content-Type: application/json

{
  "checkout_session": {
    "line_items": [{ "price": "fprice_...", "quantity": 1 }],
    "client_reference_id": "order_12345",
    "mode": "payment",
    "success_url": "https://example.com/success",
    "cancel_url": "https://example.com/cancel"
  }
}

Step 3 — Send a synthetic EFW event

Use the script below to generate a correctly signed event and POST it to your local Flex server. Flex will process it and forward the resulting radar.early_fraud_warning.created event to your registered webhook endpoint.
import hmac
import hashlib
import json
import time
import urllib.request

# Your webhook signing secret (from your .env or Flex dashboard)
WEBHOOK_SECRET = "whsec_your_signing_secret_here"
FLEX_WEBHOOK_URL = "http://localhost:8000/v1/webhooks/stripe"

CHARGE_ID = "ch_..."   # The underlying charge ID — see note below
PI_ID = "pi_..."       # The underlying payment intent ID — see note below

timestamp = int(time.time())

payload = {
    "id": "evt_synthetic_efw_001",
    "object": "event",
    "api_version": "2023-10-16",
    "created": timestamp,
    "livemode": False,
    "pending_webhooks": 1,
    "request": {"id": None, "idempotency_key": None},
    "type": "radar.early_fraud_warning.created",
    "data": {
        "object": {
            "id": "efw_synthetic_001",
            "object": "radar.early_fraud_warning",
            "actionable": True,
            "charge": CHARGE_ID,
            "created": timestamp,
            "fraud_type": "card_never_received",
            "livemode": False,
            "payment_intent": PI_ID
        }
    }
}

body = json.dumps(payload, separators=(',', ':'))
signed_payload = f"{timestamp}.{body}"
mac = hmac.new(
    WEBHOOK_SECRET.encode('utf-8'),
    signed_payload.encode('utf-8'),
    hashlib.sha256
)
sig_header = f"t={timestamp},v1={mac.hexdigest()}"

req = urllib.request.Request(
    FLEX_WEBHOOK_URL,
    data=body.encode('utf-8'),
    headers={
        'Content-Type': 'application/json',
        'Stripe-Signature': sig_header,  # required header name for webhook signature
    },
    method='POST'
)

with urllib.request.urlopen(req) as resp:
    print(f"Status: {resp.status}")  # expect 200
Where to find the charge and payment intent IDs: After completing a payment in the Flex checkout, look at the checkout_session response — the payment_intent field contains the payment intent ID (pi_...). The corresponding charge ID (ch_...) can be retrieved by fetching the payment intent from the Flex API.

Step 4 — Verify the event arrives at your endpoint

Your webhook handler should receive:
{
  "event_id": "fevt_...",
  "event_type": "radar.early_fraud_warning.created",
  "event_dt": 1770000000,
  "object": {
    "early_fraud_warning_id": "fefw_...",
    "charge_id": "fch_...",
    "payment_intent_id": "fpi_...",
    "actionable": true,
    "fraud_type": "card_never_received",
    "client_reference_id": "order_12345",
    "test_mode": false,
    "created_at": "2026-02-15T10:20:00Z"
  }
}
Verify:
  • early_fraud_warning_id has fefw_ prefix (Flex ID)
  • charge_id and payment_intent_id are Flex IDs (fch_, fpi_)
  • client_reference_id matches what you set on the checkout session (if applicable)
  • actionable: true

Step 5 — Test the updated event

Re-send with actionable: false and type: "radar.early_fraud_warning.updated", keeping the same id in data.object:
payload["type"] = "radar.early_fraud_warning.updated"
payload["data"]["object"]["actionable"] = False
Verify:
  • Your endpoint receives radar.early_fraud_warning.updated
  • The same early_fraud_warning_id is in the event (upserted, not duplicated)
  • actionable is now false

Step 6 — Test idempotency

Resend the same radar.early_fraud_warning.created event (same id in data.object — e.g. efw_synthetic_001 — with a different event timestamp). Verify your handler processes it without creating a duplicate record.

Step 7 — Verify client_reference_id propagation

If you created the checkout session with a client_reference_id:
  • Confirm it appears in the object of the EFW event
  • This lets you correlate the EFW directly to your internal order without any additional lookups

Checklist

ScenarioExpected outcome
created event received and parsedfefw_ ID in object, all fields populated
client_reference_id set on checkout sessionAppears in EFW event object.client_reference_id
updated event changes actionable: falseSame fefw_ ID, actionable updated
Duplicate created event (same EFW ID)No duplicate — idempotent upsert
Missing signature header400 Bad Request from Flex
Wrong signing secret400 Bad Request from Flex
EFW referencing a charge not in FlexEvent accepted (200), EFW not persisted, error logged