Skip to main content
Intra-PSP transfers (also called P2P) are Pix transfers where the payer and payee belong to the same participant — the source and destination ISPB are identical. Because the money never leaves the institution, these transfers are settled internally instead of being routed to BTG for settlement. They must still be reported to BACEN for regulatory compliance.
Earlier plugin versions rejected intra-PSP transfers with error PIX-0406: Intra PSP Not Supported. That error has been removed — intra-PSP transfers and refunds are now fully supported.

Detection


The plugin flags a transfer as intra-PSP when the destination ISPB equals your configured PIX_ISPB:
Initiation typeSource of destination ISPB
KEY / QR_CODEDICT lookup response (account.participant)
MANUALdestination.ispb in the request payload
When detected, the initiation response includes isIntraPSP: true:
POST /v1/transfers/cashout/initiate
201 Created
{
  "id": "uuid",
  "status": "PENDING",
  "isIntraPSP": true,
  "expiresAt": "2026-02-25T12:05:00Z"
}
The isIntraPSP flag is propagated to the Transfer, Cashout, Cashin, and Refund entities for querying and reconciliation.

Processing model


Intra-PSP transfers follow the same Midaz routing as external transfers (through the @external transit account), so the ledger behavior is identical. The only difference is that settlement happens synchronously inside the plugin instead of via BTG webhooks. Two Midaz transactions are created per transfer:
  1. Cashoutsource → @external (pending: false)
  2. Cashin@external → destination (pending: false)
The internal cash-in reuses the exact same CashinApprovalCommand and CashinSettlementCommand pipelines as external cash-ins, including CRM alias validation, balance checks, Pix key ownership, collection completion, and fee calculation.

Flow


Process Cashout (isIntraPSP = true)
  → Midaz debit: source → @external
  → Write inbound record to the webhook queue
  → Cashout status → PROCESSING (intermediary)
  → Return PROCESSING to client

Inbound worker (existing)
  → Picks up the record and delivers it (HTTP + HMAC)
    to POST /v1/payment/webhooks/intra-psp/events

Intra-PSP endpoint (orchestrates the full lifecycle)
  → CashinApprovalCommand → ACCEPTED / DENIED
  → ACCEPTED  → CashinSettlementCommand → Midaz credit → Cashout COMPLETED
  → DENIED / settlement fails → revert Midaz debit → Cashout FAILED
  → Report to BTG TRCK002 (async, non-blocking)
  → Outbound webhooks: cashout.completed/failed + cashin.completed
The cashout responds with PROCESSING, exactly like an external cashout awaiting BTG. The final status (COMPLETED/FAILED) is delivered asynchronously via outbound webhook. The intra-PSP endpoint is fully idempotent, so worker retries never duplicate transactions.

TRCK002 regulatory reporting


Every successful intra-PSP transaction is reported to BACEN through BTG’s TRCK002 abstraction endpoint, within the regulatory SLA of P99 ≤ 300 seconds.
  • TRCK002 reporting is non-blocking — a reporting failure never rolls back the Midaz transaction or transfer completion. Failed reports are retried with exponential backoff.
  • BTG returns a PixInternalTransactionsReport entity with a pactualId and an initial status of PROCESSING.
  • Report status updates arrive via a CAMT025 webhook (PixInternalTransactionsReport) on the BTG events endpoint, transitioning the report to CONFIRMED or ERROR.
  • As a fallback when webhooks are missed, report status is queryable by end-to-end ID or return identification.

Intra-PSP refunds


A refund (devolução) for a transfer whose original cash-in was intra-PSP is also processed internally:
  • The plugin detects intra-PSP when the original cash-in has isIntraPSP = true.
  • It debits the refund requester (requester → @external), then orchestrates the refund cash-in to the original sender through the same queue + endpoint pattern.
  • The refund is reported to TRCK002 with a returnIdentification.
  • Outbound webhooks: refund.completed/refund.failed to the requester and cashin.completed to the original sender.
Unblock is not supported for intra-PSP transfers. The unblock flow queries BTG for status, which does not apply to internal transactions, so calling it returns error PIX-0418. See Refund operations.

Failure reasons


Cash-in validation failures are delivered asynchronously via the cashout.failed webhook. Common intra-PSP failure reasons:
CodeDescription
RECIPIENT_ACCOUNT_INVALIDCRM alias validation failed (account not found, closed, blocked)
RECIPIENT_CANNOT_RECEIVELedger balance validation failed
PIX_KEY_OWNERSHIP_INVALIDPix key does not belong to the destination account
COLLECTION_INVALIDDynamic QR code collection validation failed
INTERNAL_CREDIT_FAILEDLedger credit transaction failed
INTERNAL_DEBIT_REVERT_FAILEDCritical — debit revert failed; requires manual reconciliation

Next steps