DEV Community

Cover image for MED 2.0 for devs: entities, fields and the implementation flow
Lucas Aguiar for Woovi

Posted on

MED 2.0 for devs: entities, fields and the implementation flow

If you work with Pix at a fintech, one thing is certain: sooner or later you'll have to deal with fraud. And the bar the Central Bank sets for that just went up. In August 2025 the MED 2.0 rules came out (starting with Resolution BCB nΒΊ 493), the new version of Pix's Special Refund Mechanism β€” optional to adopt from Nov 23, 2025 and with a mandatory rollout scheduled for 2026.

In this article I want to explain, in a simple and technical way, what the MED entities are, the fields that matter in each one and how the flow works end to end β€” with a focus on whoever is going to write the code.

🧩 First: what MED is (and why it exists)

MED (Mecanismo Especial de DevoluΓ§Γ£o β€” Special Refund Mechanism) is a regulatory obligation from the Central Bank for every Pix participant β€” banks, payment institutions, fintechs, anyone who does Pix.

The idea is simple: when fraud or an operational failure happens, there must be a standardized and auditable path to return the victim's money. Without MED, each institution would solve it its own way (or not at all).

Important: MED is not for commercial disputes, buyer's remorse, or "I sent a Pix to the wrong person because I wasn't paying attention." It's for:

  • Scam / fraud (phishing, social engineering, coercion)
  • Account takeover
  • Fraudulent access to channels
  • Institution's operational error (duplicate Pix, wrong amount from a system failure)

πŸ’‘ Rule of thumb: MED = proven fraud or an error by the institution. It's not a "cancel my Pix" channel.

πŸš€ What changes in MED 2.0

MED 1.0 had an Achilles' heel: it only blocked and refunded at the first receiving account of the fraudulent Pix. So the scammer pulled the classic move β€” received the money and scattered it in a cascade across many accounts in seconds. By the time the block arrived, the money had already evaporated. According to the BCB, in 2025 only 9.3% of the disputed amount was recovered on average.

MED 2.0 tackles exactly that: it now traces the money's path beyond the first account. The big highlights:

  1. Multi-layer tracing. DICT follows the money through the transaction graph and picks the transactions most likely to be on the drain path.
  2. Targeted blocking. The amount is blocked at each node of the path β€” only what matters, not the whole account (more on this below).
  3. Refund in its own name. The receiver's institution debits the mule account and refunds the victim directly, appearing as the payer on the statement.
  4. Digital self-service. The harmed customer triggers the process from the app.
  5. Standardized deadlines and UX (Timing Manual and Minimum UX Requirements).

🧱 The MED entities (and how they connect)

Before diving in, the most important thing is to understand the end-to-end flow. It's what gives meaning to each entity:

  1. The harmed customer contacts their PSP, which opens a Funds Recovery (FundsRecovery). This is the starting point.
  2. DICT traces the transactions from the root transaction and sends Infraction Reports (InfractionReport) to the PSPs of the accounts along the money's path.
  3. Each receiving PSP blocks the balance (only the indicated amount) and analyzes the Infraction Report.
  4. Once the infractions are analyzed, the paying PSP triggers the start of the Refund Requests β€” and the money goes back to the victim, layer by layer.
  5. Along the way, fraud markers are recorded in DICT, feeding prevention.

So there are four entities. Understand these four and you understand MED:

Entity Base endpoint Role in the flow
Funds Recovery /funds-recoveries/ Opens the case. The harmed customer starts it; it traces and orchestrates everything (the 2.0 highlight).
Infraction Report /infraction-reports/ What each PSP in the chain receives. Triggers the block and the analysis.
Refund Request /refunds/ Executes the refund of the money to the victim, layer by layer.
Fraud Marker (automatic on agreement) Flags receivers as fraudsters in DICT, feeding prevention.

Let's go through them one by one, in the order they appear in the flow.

πŸ•ΈοΈ Funds Recovery (FundsRecovery)

This is the big novelty of MED 2.0 β€” and where everything begins. When the customer is a fraud victim, the paying PSP opens a Funds Recovery (POST /funds-recoveries/). From there, DICT traces the money and orchestrates blocks and refunds.

The stages (this is where the "several statuses" live)

Funds Recovery isn't a single step β€” it moves through stages with their own statuses, and your backend needs to react to each transition. Today the flow is fully automated: DICT does the tracing, prioritization and dispatch of the Infraction Reports with no manual step from the PSP.

Status Stage
CREATED Funds Recovery created over the root transaction
TRACKED Transaction tracking graph created
AWAITING_ANALYSIS Infraction Reports generated; receiving PSPs analyzing (up to 7 days)
ANALYSED Infraction Reports analyzed and closed; the paying PSP can trigger the refund

After ANALYSED comes the refund (requests run sequentially) and, at the end, DICT signals completion through the FUNDS_RECOVERY_COMPLETED event.

Tracking graph parameters

Parameters that bound the graph tracing (applied by DICT's algorithm):

Parameter What it controls
MinTransactionAmount Minimum amount of each transaction considered
MaxTransactions Maximum number of transactions in the graph
HopWindow Time window between money entering and leaving an account
MaxHops Maximum graph depth (how many layers to go down)

Main endpoints

Method + route What it does
POST /funds-recoveries/ Create a Funds Recovery
GET /funds-recoveries/{id} Query a Funds Recovery
POST /funds-recoveries/{id}/tracking-graph Create a tracking graph
GET /funds-recoveries/{id}/tracking-graph Query the graph
POST /funds-recoveries/{id}/block Request the transaction blocks
GET /funds-recoveries/{id}/infraction-reports List the Infraction Reports of the recovery
POST /funds-recoveries/{id}/refund Request a refund within the recovery
GET /funds-recoveries/{id}/refunds List the refunds of the recovery
POST /funds-recoveries/{id}/cancel Cancel the recovery

πŸ”” Don't sit there polling. DICT has an events endpoint (GET /event-notifications) with two key signals for the recovery:

  • FUNDS_RECOVERY_ANALYSED β†’ the Infraction Reports were analyzed; you evaluate the result and trigger (or not) the start of the refund.
  • FUNDS_RECOVERY_COMPLETED β†’ the refund phase is over; anyone still holding blocked funds can release them (if there's no other basis to keep the block).

πŸ“’ Infraction Report (InfractionReport) β€” the block

If Funds Recovery is the customer pulling the trigger, the Infraction Report is what lands on the PSPs of the accounts along the money's path. Each prioritized transaction generates an Infraction Report for the PSP of that node, which then blocks the balance and analyzes the case.

Main fields

Field What it is
TransactionId EndToEndId of the suspicious transaction (e.g., E999990101234...)
InfractionType Type: FRAUD, REFUND_REQUEST, REFUND_CANCELLED
FundsRecoveryId (MED 2.0) links the report to the Funds Recovery
InfractionAmount (MED 2.0) the amount that must be blocked from that transaction
InfractionData.DebitedParticipant / CreditedParticipant ISPBs of the payer and the receiver
InfractionData.ReportedBy DEBITED_PARTICIPANT or CREDITED_PARTICIPANT
InfractionData.TransactionDate Transaction date-time
InfractionData.InfractingAccountData Branch + account involved at the node
TransactionDepth (latest) position of the transaction in the money-movement chain β€” how far it is from the initial fraud (DICT Manual v8.5)
Status OPEN, ACKNOWLEDGED, CLOSED, CANCELLED
AnalysisResult AGREED or DISAGREED

The block: only the InfractionAmount, not the whole account

This is the part that causes the most bugs if you're not careful. What you block is not the account's entire balance, nor the full amount of the original transaction β€” it's exactly the InfractionAmount: the amount that node received and that needs to be held on that specific transaction.

Why? Because the money spreads out. A mule account may have received R$ 800 from one branch and R$ 200 from another; each Infraction Report carries the right amount to block on each transaction. Blocking too much holds the customer's legitimate money; blocking too little lets funds slip away. Block the InfractionAmount, no more, no less.

⏱️ The receiving PSP has up to 7 days to analyze and close the report (.../acknowledge on receipt, .../close with AGREED/DISAGREED). Every agreement generates a fraud marker for that receiver β€” even if they never end up receiving a refund request.

πŸ’Έ Refund Request (Refund) β€” bringing the money back

Once the Infraction Reports are analyzed (FUNDS_RECOVERY_ANALYSED event), the paying PSP triggers the start of the refund. From there DICT fires the Refund Requests sequentially, without you having to orchestrate them one by one.

The key point: each refund goes to the harmed customer, only for the missing amount

Here lies the biggest mindset shift of 2.0. Every refund request is destined to the harmed customer (the user of the paying PSP who opened the recovery), and the goal is to return the money wherever it is in the chain. Rules that show up on the test:

  • The 1st request always goes to the receiver of the root transaction.
  • The subsequent ones follow the priority defined in the blocking stage.
  • Each PSP debits the mule account and refunds directly to the harmed customer, appearing as the payer on the statement.
  • Only the missing amount is requested to complete the total β€” never the full initial amount again. BCB's own example: even with R$ 800 blocked at a node, if R$ 250 was already recovered, the refund is requested for the remaining R$ 750 only. The amounts are updated automatically with each new refund.

In other words: the system keeps "scraping" whatever it can from each account until the total is met, never refunding more than owed.

Main fields

Field What it is
TransactionId Transaction being refunded
RefundReason FRAUD, OPERATIONAL_FLAW, REFUND_CANCELLED
RefundAmount Requested amount β€” the remainder, not the full value
FundsRecoveryId (MED 2.0) links the refund to the recovery
RefundAccount (MED 2.0) destination account for the payment (the harmed customer's)
MonitorAccount (MED 2.0) whether the account should be monitored for future blocks
EffectiveRefundedAmount (MED 2.0) on closure, the amount actually refunded
Status OPEN, CLOSED, CANCELLED
AnalysisResult TOTALLY_ACCEPTED, PARTIALLY_ACCEPTED, REJECTED
RefundRejectionReason NO_BALANCE, ACCOUNT_CLOSURE, CANNOT_REFUND, OTHER

πŸ’‘ Partial refund + monitoring is the nastiest real-world case. If the account doesn't have the full balance at the moment (PARTIALLY_ACCEPTED / NO_BALANCE), you accept what you can, report the EffectiveRefundedAmount and, with MonitorAccount on, monitor the account to keep blocking and refunding in parts as money comes in. Model this from the start β€” it's not an exception, it's routine.

🚩 Fraud Marker (prevention)

MED isn't only about putting out fires β€” it feeds prevention.

When an Infraction Report is agreed (AGREED), the receiver is flagged as a fraudster in DICT, automatically. And there's an important 2.0 detail: all receivers of transactions with an agreed report get flagged β€” even the ones that never receive a refund request (e.g., a node that fell outside the continuous path of the graph).

These markers feed DICT's anti-fraud data (counters of settlements, reported frauds and confirmed frauds, by key, by owner and by account).

🎯 Where this becomes gold: query that anti-fraud data for the destination key/account before releasing a cash-out. A recent fraud signal? You hold, ask for a double-check or add friction. Preventing is far cheaper than running the whole recovery flow.

πŸ“… The deadlines you CAN'T miss

A blown deadline in MED = money lost or a regulatory fine. Nail these into your scheduler (the definitive source is the Pix Timing Manual):

Rule Deadline
Maximum transaction age to open a case 80 days after the Pix
Infraction Report analysis by the receiving PSP up to 7 days
Start the refund after the reports are analyzed up to 72 hours

Strong recommendation: model each deadline as a scheduled job with alerts. Don't rely on someone watching a spreadsheet. A due SLA is a system event.

βœ… When to use (and NOT use) MED

You can use it for:

  • Proven scam/fraud
  • Account takeover / coercion / fraudulent access
  • The institution's own operational error

You can't use it for:

  • Commercial disputes ("the product never arrived")
  • Buyer's remorse
  • A Pix sent to the wrong person by the payer's own mistake
  • Disputes between good-faith third parties

🏁 Wrapping up

MED 2.0 changes the game of fraud recovery on Pix: gone is the focus on a single account, in comes tracing the money's path, with targeted blocking and layer-by-layer refunds. For your fintech, the summary is:

  • 🧭 The flow starts with the customer: they open the Funds Recovery, which traces and fires the Infraction Reports.
  • πŸ”’ Block the InfractionAmount, not the whole account β€” that's the detail that bites the most.
  • πŸ’Έ Each refund goes to the harmed customer and only for the missing amount β€” the goal is to bring the money back wherever it is.
  • πŸ”” Use events, not polling (FUNDS_RECOVERY_ANALYSED, FUNDS_RECOVERY_COMPLETED).
  • πŸ“š The source of truth is always the DICT API spec and the BCB rules β€” check the current version before coding.

Official sources (BCB)

That's it! If you're implementing MED 2.0 at your fintech and hit a wall, drop a comment and let's talk it through. πŸš€

Top comments (0)