Table of Contents
- Overview
- How EFWs Differ from Disputes
- The EarlyFraudWarning Object
- Fraud Type Reference
- Webhook Events
- Recommended Response Strategy
- End-to-End Testing Guide
Overview
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: truemeans the charge has not yet been refunded or disputed — act now.actionable: falsemeans 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 everyradar.early_fraud_warning.* webhook event.
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.
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).
Webhook signature verification
All Flex webhooks include aflex-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 onearly_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 receiveradar.early_fraud_warning.created with actionable: true:
1. Assess the risk
Check thefraud_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_typehigh-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 thecharge_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 — monitorfraud_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.createdradar.early_fraud_warning.updated
Step 2 — Create a charge
Create a checkout session and complete a payment. Note thecharge_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:
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 resultingradar.early_fraud_warning.created event to your registered webhook endpoint.
Where to find the charge and payment intent IDs: After completing a payment in the Flex checkout, look at thecheckout_sessionresponse — thepayment_intentfield 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:early_fraud_warning_idhasfefw_prefix (Flex ID)charge_idandpayment_intent_idare Flex IDs (fch_,fpi_)client_reference_idmatches what you set on the checkout session (if applicable)actionable: true
Step 5 — Test the updated event
Re-send withactionable: false and type: "radar.early_fraud_warning.updated", keeping the same id in data.object:
- Your endpoint receives
radar.early_fraud_warning.updated - The same
early_fraud_warning_idis in the event (upserted, not duplicated) actionableis nowfalse
Step 6 — Test idempotency
Resend the sameradar.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
objectof 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 |