Early Fraud Warnings - Flex Documentation

Early Fraud Warnings (EFWs)

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


How EFWs Differ from Disputes

Early Fraud Warning Dispute (Chargeback)
Trigger Card issuer files fraud report with card network Cardholder formally disputes with their bank
Impact on funds No immediate fund withdrawal Funds withdrawn immediately
Fee No fee Dispute fee charged
Flex API Webhook only Full REST API
Recommended action Consider proactive refund Submit evidence or accept loss
Time pressure Respond before a formal dispute is filed Evidence 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

Field Type Description
early_fraud_warning_id string Flex EFW ID (fefw_ prefix).
charge_id string  null Flex charge ID (fch_) for the flagged transaction.
payment_intent_id string  null Flex payment intent ID (fpi_) linked to the charge.
actionable boolean true if the charge is still refundable and no dispute has been filed. false once refunded or disputed.
fraud_type string Fraud category from the card issuer.
client_reference_id string  null The client_reference_id you passed when creating the checkout session, if any.
test_mode boolean true for test-mode transactions.
created_at string ISO 8601 timestamp when Flex received the EFW.

Fraud Type Reference

Value Description
card_never_received Physical card was never received by the cardholder (e.g. intercepted in mail).
fraudulent_card_application Card was obtained through a fraudulent application.
made_with_counterfeit_card Physical counterfeit card was used.
made_with_lost_card Lost card was used without authorisation.
made_with_stolen_card Stolen card was used without authorisation.
unauthorized_use_of_card Card was used without the cardholder’s authorisation (general).
misc Other / 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.


Recommended Response Strategy

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.

2. Decide: refund or wait

Scenario Recommendation
Order not yet shipped Cancel and refund immediately.
Order shipped, high-value, high-risk fraud type Proactive refund may still be cheaper than losing a dispute + fee.
Order shipped, low-value Weigh refund cost vs dispute fee (~$15) + chargeback rate impact.
actionable: false No 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

Step 1 — Register a webhook endpoint

In your Flex dashboard, register your endpoint URL and subscribe to:

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:

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:

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:


Checklist

Scenario Expected outcome
created event received and parsed fefw_ ID in object, all fields populated
client_reference_id set on checkout session Appears in EFW event object.client_reference_id
updated event changes actionable: false Same fefw_ ID, actionable updated
Duplicate created event (same EFW ID) No duplicate — idempotent upsert
Missing signature header 400 Bad Request from Flex
Wrong signing secret 400 Bad Request from Flex
EFW referencing a charge not in Flex Event accepted (200), EFW not persisted, error logged