Skip to main content
The Pix Indirect Plugin (BTG) processes Pix refunds (devoluções) through the existing Refund a Received Pix Transfer endpoint. Two capabilities extend that flow for real-world MED and fraud scenarios:
  • Distributed partial refunds — return a refund from multiple internal accounts in a single operation
  • Unblock — recover a refund or transfer that is stuck in PROCESSING

Distributed partial refunds


When a received Pix is distributed internally across several accounts — for example, the principal amount to the customer’s account and a fee to a fee account — a later MED/fraud refund may need to be debited partially from each of those accounts. The standard refund flow debits from a single account. To split the debit, send an optional operations array in the request body. The presence of operations is the only signal — there is no new endpoint, env var, or feature flag.

Request


POST /v1/transfers/{transfer_id}/refunds
{
  "amount": "1000.82",
  "description": "MED Cappta",
  "operations": [
    { "accountAlias": "alias-conta-cliente", "amount": "0.82" },
    { "accountAlias": "alias-conta-fee", "amount": "1000.00" }
  ]
}
  • Without operations → the current single-account flow runs unchanged.
  • With operations → the plugin debits each accountAlias for its amount in Midaz, and BTG receives a single pacs.004 for the total refund value.

Validation rules


RuleError
sum(operations[].amount) must equal amount400 OPERATIONS_SUM_MISMATCH
accountAlias must not repeat within the request400 DUPLICATE_ACCOUNT_ALIAS
each amount must be greater than 0400
each amount must have at most 2 decimal places400
each accountAlias must have participated in the original transaction422 ACCOUNT_ALIAS_NOT_IN_ORIGINAL_TRANSACTION
the original transaction must exist in Midaz422 ORIGINAL_TRANSACTION_NOT_FOUND
The endpoint, BTG flow (pacs.004 with the total value), idempotency, and authentication are identical to the standard refund. Only the internal Midaz debit composition changes.

Example — Cappta


A R50,000.00cashinwassplitasR 50,000.00 cash-in was split as R 49,000.00 to the customer account and R1,000.00toafeeaccount.Afterfraud,therequiredrefundisR 1,000.00 to a fee account. After fraud, the required refund is R 1,000.82, but only R0.82remainsinthecustomeraccount.TheoperationsarraydebitsR 0.82 remains in the customer account. The `operations` array debits R 0.82 from the customer account and R1,000.00fromthefeeaccount,whileBTGreceivesasingleR 1,000.00 from the fee account, while BTG receives a single R 1,000.82 refund — no manual ledger consolidation required.

Unblocking stuck operations


A refund or transfer whose reversal call to BTG times out before confirmation can remain stuck in PROCESSING, with the hold still applied in Midaz. Two dedicated endpoints re-query BTG and drive the operation to its correct terminal state.
MethodEndpointUnblocks
POST/v1/refunds/{refund_id}/unblockA refund stuck in PROCESSING
POST/v1/transfers/{transfer_id}/unblockA transfer stuck in PENDING/PROCESSING
Both require the X-Account-Id header.

How unblock works


The plugin re-queries the reversal/transfer status at BTG and:
  • If BTG reports CONFIRMED or ERROR → dispatches the corresponding settlement and moves the operation to its terminal state.
  • If BTG still reports INITIATED/PROCESSING → returns HTTP 200 with no action; retry later.
A consistency guard aborts the operation if BTG’s entity, returnIdentification, or originalEndToEndId diverge from the local record, preventing settlement against the wrong transaction.
POST /v1/refunds/{refund_id}/unblock
X-Account-Id: <account-id>
The response includes the updated refund, a message, and the btgStatus.

Handling a 404 from BTG


When BTG no longer has the reversal/transfer (returns 404), behavior depends on the operation type and the opt-in allowNotFoundUnblock flag in the request body:
OperationDefault (strict)With allowNotFoundUnblock: true
Transfer (/v1/transfers/{id}/unblock)Returns an errorReverts the Midaz hold (best-effort, idempotent), marks the cashout FAILED with BTG_NOT_FOUND, emits a CASHOUT/TRANSFER outbound webhook, and returns 200 with btgStatus: "NOT_FOUND"
Refund (/v1/refunds/{id}/unblock)Returns PIX-1012 (ErrProviderRefundNotFound)Reverts the hold and drives the refund to its terminal state
POST /v1/transfers/{transfer_id}/unblock
{
  "allowNotFoundUnblock": true
}
allowNotFoundUnblock defaults to false. An empty/absent body or false preserves the strict behavior (a BTG 404 returns an error and never reverts the hold). Only opt in when you have confirmed the operation should be released.
The opt-in 404 recovery applies only to CASHOUT operations in PENDING/PROCESSING with a non-empty endToEndId. Cash-ins and terminal statuses never enter this branch.

Intra-PSP limitation


The transfer unblock flow queries BTG for status, which does not apply to intra-PSP (internal) transfers — there is no BTG transaction. Calling unblock on an intra-PSP transfer returns a synchronous error (PIX-0418). Proper intra-PSP unblock logic is a future enhancement. See Intra-PSP transfers.

Next steps