MED Webhook (Disputes)

Get notified when a PIX you received is disputed through MED (Mecanismo Especial de Devolução), and every time the dispute changes status.

Endpoint and Authentication

POSTSame URL(s) you set through the /api/SendWebhook route

MED events go to every webhook URL configured for your company, together with the PixIn and CashOut events. Tell them apart by the object field: MED events always have "object": "med_defense".

Headers Sent

Headers
Content-Type: application/json
X-Webhook-Event: med_defense.status_changed
X-Webhook-Event-Type: med_defense.status_changed
X-Webhook-Correlation-Id: med:812:med_defense.status_changed:5f0c2a...
X-Webhook-Sent-At: 2026-11-08T14:21:09Z
X-Webhook-Secret: <your webhook secret, when configured>

Compare X-Webhook-Secret with the secret you configured to make sure the call came from Baanko.

Events

med_defense.createdtypeA new MED was opened against one of your received PIX transactions
med_defense.status_changedtypeAn existing MED changed status (under review, won, lost, refunded, etc.)

Payload

Webhook Payload - med_defense.created
{
"object": "med_defense",
"type": "med_defense.created",
"data": {
"id": 812,
"companyId": "7719342c-a3f4-443b-9c8c-a06ec7a43b7d",
"infractionId": "01M6KRS6KCKM2M3J05M1K5H628",
"status": "Pending",
"infractionType": "Fraud",
"infractionReason": "Customer claims he did not recognize the payment",
"disputedAmount": 14.14,
"currency": "BRL",
"endToEndId": "E00000000202611081421abcdef12345",
"txId": "cmw4csk200mfy6nr3eibya4va",
"claimantName": "JOHN EXAMPLE",
"claimantDocument": "123*****909",
"reportedBy": "DEBITED_PARTICIPANT",
"deadline": "2026-11-15T14:21:00Z",
"defenseSubmittedAt": null,
"resolvedAt": null,
"returnedAmount": null,
"provider": "OryxPay",
"createdAt": "2026-11-08T14:21:05Z",
"updatedAt": "2026-11-08T14:21:05Z"
}
}
Webhook Payload - med_defense.status_changed (MED lost)
{
"object": "med_defense",
"type": "med_defense.status_changed",
"data": {
"id": 812,
"companyId": "7719342c-a3f4-443b-9c8c-a06ec7a43b7d",
"infractionId": "01M6KRS6KCKM2M3J05M1K5H628",
"status": "Accepted",
"infractionType": "Fraud",
"disputedAmount": 14.14,
"currency": "BRL",
"endToEndId": "E00000000202611081421abcdef12345",
"txId": "cmw4csk200mfy6nr3eibya4va",
"deadline": "2026-11-15T14:21:00Z",
"resolvedAt": "2026-11-10T09:02:44Z",
"returnedAmount": 14.14,
"provider": "OryxPay",
"createdAt": "2026-11-08T14:21:05Z",
"updatedAt": "2026-11-10T09:02:44Z"
}
}

The second example is shortened. Every event carries all the fields listed below; fields without a value are sent as null.

Field Descriptions

objectstringAlways "med_defense"
typestringmed_defense.created or med_defense.status_changed
data.idintegerBaanko ID of the MED. Use it to match later status updates
data.companyIdstring (UUID)Your company ID
data.infractionIdstringID of the infraction / MED at the provider (BACEN)
data.statusstringCurrent MED status (see the list below)
data.infractionTypestringReason category (see the list below)
data.infractionReasonstring | nullFree text with the reason given by the payer's bank
data.disputedAmountnumberDisputed amount, in reais (14.14 = R$ 14.14)
data.currencystringAlways "BRL"
data.endToEndIdstring | nullEnd-to-end ID of the disputed PIX. Use it to find the transaction on your side
data.txIdstring | nullThe transactionId of the disputed transaction, when known
data.claimantNamestring | nullName of the person who opened the dispute
data.claimantDocumentstring | nullClaimant CPF/CNPJ, masked (first 3 and last 3 digits)
data.reportedBystring | nullWho reported the infraction, as sent by the provider
data.deadlinestring | nullDeadline to send the defense (UTC)
data.defenseSubmittedAtstring | nullWhen the defense was submitted (UTC)
data.resolvedAtstring | nullWhen the MED reached a final status (UTC)
data.returnedAmountnumber | nullAmount effectively returned to the payer, in reais
data.providerstring | nullAcquirer that received the PIX
data.createdAtstringWhen the MED was registered at Baanko (UTC)
data.updatedAtstringLast change of the MED (UTC)

MED Statuses

Values of data.status. Compare them case-insensitively.

Open (amount held)

PendingMED opened, waiting for analysis or defense
UnderReviewUnder analysis by the provider / receiving bank
WaitingAdjustmentsThe provider asked for adjustments or more information for the defense
AnsweredDefense / answer sent, waiting for the decision
DefendedDefense submitted, waiting for the decision
LateThe defense deadline has passed without an answer

Final

RejectedMED rejected: you won the dispute. The held amount is released back to your balance
CancelledMED cancelled by the payer's bank. The held amount is released after a Baanko review
ExpiredMED expired without a decision against you. The held amount is released
AcceptedMED accepted: you lost the dispute. The amount is returned to the payer and debited from your balance
ReturnedAmount returned to the payer (refund done). Debited from your balance

Effect on Your Balance

•While the MED is open, the disputed amount is held and cannot be withdrawn.
•If you win (Rejected or Expired), the held amount is released back to your available balance automatically. For Cancelled, it is released after a Baanko review.
•If you lose (Accepted or Returned), the amount is returned to the payer and stays debited from your balance.
•The original transaction is not changed by the MED webhook. Match the MED to your order through endToEndId or txId.

Infraction Types

Values of data.infractionType.

FraudFraud reported by the payer
OperationalFailureOperational failure at a bank / PIX participant
SocialEngineeringSocial engineering scam
MuleAccountReceiving account suspected of being a mule account
KidnappingPayment made under kidnapping
CoercionPayment made under coercion
RefundRequestRefund request from the payer's bank
RefundCancelledRefund request cancelled
OtherAny other reason

Delivery

•Reply with any 2xx status within 15 seconds to confirm receipt.
•MED events are sent once per change and are not retried automatically. If your endpoint was down, ask Baanko support to resend the event.
•The same MED can produce several status_changed events. Use data.id + data.status (or data.updatedAt) to ignore events you already processed.

Handler Example

Node.js / Express
const LOST = ['accepted', 'returned'];
const WON = ['rejected', 'cancelled', 'expired'];
app.post('/webhook/baanko', async (req, res) => {
if (req.headers['x-webhook-secret'] !== process.env.BAANKO_WEBHOOK_SECRET) {
return res.status(401).end();
}
const { object, type, data } = req.body;
if (object === 'med_defense') {
const status = data.status.toLowerCase();
if (type === 'med_defense.created') {
// Flag the order as disputed
await flagOrderDisputed(data.endToEndId, data.disputedAmount, data.deadline);
} else if (LOST.includes(status)) {
// Dispute lost: amount returned to the payer
await markOrderRefunded(data.endToEndId, data.returnedAmount ?? data.disputedAmount);
} else if (WON.includes(status)) {
// Dispute won: amount released
await clearDispute(data.endToEndId);
}
return res.status(200).json({ received: true });
}
// ... PixIn / CashOut events
res.status(200).json({ received: true });
});