Documentation Index
Fetch the complete documentation index at: https://docs.withflex.com/llms.txt
Use this file to discover all available pages before exploring further.
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 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. See Fraud Type Reference. |
client_reference_id | string | null | The client_reference_id you passed when creating the checkout session, if any. Useful for correlating EFWs to your internal order IDs. |
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.
- 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
| 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
- 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
| 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 |