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/jsonX-Webhook-Event: med_defense.status_changedX-Webhook-Event-Type: med_defense.status_changedX-Webhook-Correlation-Id: med:812:med_defense.status_changed:5f0c2a...X-Webhook-Sent-At: 2026-11-08T14:21:09ZX-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 transactionsmed_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_changeddata.idintegerBaanko ID of the MED. Use it to match later status updatesdata.companyIdstring (UUID)Your company IDdata.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 bankdata.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 sidedata.txIdstring | nullThe transactionId of the disputed transaction, when knowndata.claimantNamestring | nullName of the person who opened the disputedata.claimantDocumentstring | nullClaimant CPF/CNPJ, masked (first 3 and last 3 digits)data.reportedBystring | nullWho reported the infraction, as sent by the providerdata.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 reaisdata.providerstring | nullAcquirer that received the PIXdata.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 payerOperationalFailureOperational failure at a bank / PIX participantSocialEngineeringSocial engineering scamMuleAccountReceiving account suspected of being a mule accountKidnappingPayment made under kidnappingCoercionPayment made under coercionRefundRequestRefund request from the payer's bankRefundCancelledRefund request cancelledOtherAny other reasonDelivery
•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 });});