This guide walks you through everything you need to do to handle Stripe Radar fraud reviews through Flex — from subscribing to webhooks, to building an internal review queue, to testing the integration end-to-end. By the end of this guide, you will be able to: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.
- React in real time when Radar opens a review on one of your charges.
- Take the right action when a review is closed (approved, refunded, fraud, disputed).
- Build a “Reviews queue” view for your fraud-ops team using the Flex REST API.
- Join reviews back to your own order IDs without maintaining an extra mapping.
- Verify the entire flow against test mode before shipping to production.
What is a review? A review is a Stripe Radar fraud-review record surfaced through Flex. When Radar flags one of your charges — either via a Radar rule or a manual review action in Stripe — Flex persists the review and notifies you via a webhook event and via a queryable record on the Flex REST API. A review is open while the decision is pending, and closed once it has been resolved (approved, refunded, marked as fraud, disputed, or redacted).
Before you begin
Make sure you have:- A Flex API key with the
reviews:readscope. Add it to an existing key in the Flex dashboard, or include it when minting a new key. - A webhook endpoint registered with Flex that can receive
review.openedandreview.closedevents. - (Recommended)
client_reference_idset on yourPOST /v1/checkout/sessionscalls — this is the single highest-leverage change you can make for joining reviews back to your internal order IDs.
Reviews are read-only in Flex. Approving a review must be done in the Stripe dashboard — there is no
POST /v1/reviews/:id/approve endpoint.How the flow works
Before wiring anything up, here is the lifecycle you are integrating against:Stripe Radar opens a review
Triggered by a Radar rule firing, or by a teammate opening a manual review in the Stripe dashboard.
You receive the event in two places
A
review.opened webhook is forwarded to your endpoint, and GET /v1/reviews immediately returns the new record.The review is resolved in Stripe
Someone refunds, approves, accepts the dispute, or redacts the review.
Step 1 — Subscribe to the webhook events
In your Flex webhook configuration, subscribe to:review.openedreview.closed
review.opened payload looks like this:
review.opened
review.closed payload adds closed_reason and flips open to false:
review.closed
Verify the webhook signature
Flex signs every outbound webhook so you can confirm it actually came from Flex. The canonical delivery path uses Svix and includessvix-id, svix-timestamp, and svix-signature headers — see the Verifying Webhooks guide for the full verification flow and SDK examples.
Make your handler idempotent
Stripe and Flex both retry webhooks. Your handler should be safe to invoke twice with the same payload.| Scenario | What to expect |
|---|---|
| Same event delivered twice | Be idempotent on event_id. |
review.opened retried after review.closed already processed | Closed state is preserved. open stays false, closed_reason retained. |
| Late-arriving event with stale data | partner_id, charge_id, payment_intent_id, and created_at are write-once and not overwritten. |
Step 2 — Handle review.opened
When a review opens, the charge is still captured but Stripe (or your own ops team) has flagged it for human review. You usually want to pause downstream side effects until the review resolves.
A typical handler:
- Look up the order in your system using
object.review.client_reference_id(or fall back topayment_intent_id). - If the order has not yet shipped or been provisioned, hold fulfilment until the review closes.
- Optionally surface the review in your internal admin tool so an ops person can investigate.
Step 3 — Handle review.closed
When you receive a review.closed, branch on closed_reason and take the matching action:
closed_reason | What it means | What to do |
|---|---|---|
approved | The charge was reviewed and approved. Funds remain captured. | Release any held fulfilment and continue normally. |
refunded | The charge was refunded as part of closing the review. | Cancel the order in your system if you have not already. |
refunded_as_fraud | Refunded and explicitly marked as fraud. Feeds back into Radar’s ML model. | Cancel the order, and consider flagging the customer / IP / device. |
disputed | The cardholder disputed the charge before the review was manually closed. | Switch to the dispute response workflow — see the Disputes API guide. |
redacted | Rare — typically a data-removal request. | No customer-facing action; remove related records if relevant. |
Step 4 — Build a “Reviews queue” view
Webhooks tell you what just happened, but for an ops dashboard you also want to ask “what is open right now?” Use the REST API for this.Authenticate
| Requirement | Detail |
|---|---|
| Auth type | Bearer token (Flex API key) |
| Required scope | reviews:read |
List open reviews
Poll for everything currently in review:- See which orders are currently in review.
- Click through to the corresponding charge / order in your admin tools.
- Identify reviews that have been open unusually long.
GET /v1/reviews?open=false filtered by date for a historical audit log.
Paginate through the results
Reviews are returned newest-first as a flat array — there is nohas_more field.
limit items, you have reached the end. Use ending_before for backward pagination.
Available filters
| Parameter | Type | Use it to… |
|---|---|---|
open | boolean | Return only open (true) or only closed (false) reviews. Omit for both. |
charge_id | string | Filter to reviews on a specific Flex charge. |
payment_intent_id | string | Filter to reviews on a specific Flex payment intent. |
client_reference_id | string | Filter to reviews tied to one of your order IDs. |
starting_after | string | Forward pagination cursor (a review_id). |
ending_before | string | Backward pagination cursor. |
limit | integer | Page size, between 1 and 100. Default 20. |
Audit a single review
When you need to look up one review by Flex ID:Response
Handle errors
| Status | When |
|---|---|
401 Unauthorized | API key is missing or lacks the reviews:read scope. |
404 Not Found | Review does not exist, or belongs to another partner, or test_mode does not match the API key being used. (Cross-partner existence is never leaked.) |
Step 5 — Join reviews to your order IDs
The cleanest way to tie a review back to your own system is viaclient_reference_id. Set it on the checkout session when you create it:
Step 6 — Test the integration end-to-end
Stripe does not open reviews on standard test card payments. Use one of the methods below to generate a review event end-to-end.
Method A — stripe trigger
If you have the Stripe CLI authenticated against your Stripe test account, fire a synthetic event:
review.opened to your endpoint.
Then verify the review landed in Flex:
frv_… ID.
Method B — Synthetic webhook (local development)
For local development againstlocalhost, send a signed synthetic Stripe webhook directly to your Flex server. The event differences for Reviews:
| Field | Value |
|---|---|
type | "review.opened" (or "review.closed") |
data.object | A Stripe review object (id starts with prv_, includes charge, reason, opened_reason, open, etc.) |
data.object for a review.opened:
review.closed, set open: false, reason: "refunded_as_fraud", and closed_reason: "refunded_as_fraud". Keep the same id so Flex upserts onto the existing record rather than creating a new one.
Verification checklist
Walk through these scenarios before considering your integration done:| Scenario | Expected outcome |
|---|---|
review.opened received | New frv_… review in DB; webhook forwarded with object.review.open: true |
client_reference_id set on checkout session | Appears in object.review.client_reference_id on the forwarded webhook |
review.closed received | Same frv_… review updated; object.review.open: false, closed_reason set; webhook forwarded |
Out-of-order: review.opened retried after review.closed | DB row stays closed; open does not flip back to true |
Duplicate review.opened (same prv_ ID) | No duplicate row — idempotent upsert |
GET /v1/reviews?open=true after review.closed | Closed review is no longer in the response |
GET /v1/reviews/frv_… for another partner’s review | 404 Not Found |
API key without reviews:read scope | 401 Unauthorized on /v1/reviews |
| Test-mode key reading a live review (or vice-versa) | 404 Not Found |
Reference
The review object
Flex review ID (
frv_ prefix). Use this in REST calls.Flex charge ID (
fch_) for the reviewed transaction.Flex payment intent ID (
fpi_) linked to the charge.Flex partner that owns the review.
Current high-level reason. Tracks
opened_reason while open; tracks closed_reason once closed.Why the review was opened. One of
rule, manual.Why the review was closed. Present only when
open: false. One of approved, disputed, redacted, refunded, refunded_as_fraud.true while the review is pending. false once resolved.Billing ZIP captured at payment, when present.
IP address captured at payment, when present.
The
client_reference_id you set on the underlying checkout session.true for test-mode reviews.ISO 8601 timestamp when Flex first persisted the review.
Reason codes
opened_reason
| Value | Meaning |
|---|---|
rule | A Radar rule fired and opened the review automatically. |
manual | A teammate opened the review manually from the Stripe dashboard. |
closed_reason (set once open: false)
| Value | Meaning |
|---|---|
approved | The charge was reviewed and approved. Funds remain captured. |
refunded | The charge was refunded as part of closing the review. |
refunded_as_fraud | Refunded and explicitly marked as fraud — feeds back into Radar’s ML model. |
disputed | The cardholder disputed the charge (chargeback) before the review was manually closed. |
redacted | The review was redacted (rare; e.g. data-removal request). |
Reviews vs. Early Fraud Warnings
A single charge may have both a review and an EFW. They are independent signals — subscribe to both for full visibility.| Review | Early Fraud Warning | |
|---|---|---|
| Trigger | Radar rule fires, or a teammate opens a manual review in Stripe | Card issuer files a fraud report with the network |
| State model | open → closed (with closed_reason) | actionable: true → actionable: false |
| Implies a chargeback? | No — Stripe-internal flag for human review | No, but is a stronger signal one may follow |
| REST API | GET /v1/reviews, GET /v1/reviews/:id | None (webhook only) |
| Webhook events | review.opened, review.closed | radar.early_fraud_warning.created / .updated |
| Required auth scope | reviews:read | n/a |
What is not forwarded
- Stripe reviews that have no associated charge are silently dropped (no DB write, no webhook). This is rare but possible during certain failure modes.
- Reviews on charges belonging to partners not onboarded to Flex are not forwarded.