> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Intra-PSP transfers

> How the Pix Indirect Plugin (BTG) processes intra-PSP (P2P) transfers and refunds internally — without BTG settlement — while still reporting to BACEN via TRCK002.

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.

<Note>
  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.
</Note>

# Detection

***

The plugin flags a transfer as intra-PSP when the destination ISPB equals your configured `PIX_ISPB`:

| Initiation type   | Source of destination ISPB                   |
| ----------------- | -------------------------------------------- |
| `KEY` / `QR_CODE` | DICT lookup response (`account.participant`) |
| `MANUAL`          | `destination.ispb` in the request payload    |

When detected, the initiation response includes `isIntraPSP: true`:

```json theme={null}
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. **Cashout** — `source → @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
```

<Note>
  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.
</Note>

# 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.

<Warning>
  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](/en/midaz/plugins/pix/indirect/indirect-pix-refund-operations).
</Warning>

# Failure reasons

***

Cash-in validation failures are delivered asynchronously via the `cashout.failed` webhook. Common intra-PSP failure reasons:

| Code                           | Description                                                      |
| ------------------------------ | ---------------------------------------------------------------- |
| `RECIPIENT_ACCOUNT_INVALID`    | CRM alias validation failed (account not found, closed, blocked) |
| `RECIPIENT_CANNOT_RECEIVE`     | Ledger balance validation failed                                 |
| `PIX_KEY_OWNERSHIP_INVALID`    | Pix key does not belong to the destination account               |
| `COLLECTION_INVALID`           | Dynamic QR code collection validation failed                     |
| `INTERNAL_CREDIT_FAILED`       | Ledger credit transaction failed                                 |
| `INTERNAL_DEBIT_REVERT_FAILED` | Critical — debit revert failed; requires manual reconciliation   |

# Next steps

***

* [Webhooks](/en/midaz/plugins/pix/indirect/indirect-pix-webhooks) — Event envelope, retries, and routing
* [Refund operations](/en/midaz/plugins/pix/indirect/indirect-pix-refund-operations) — Distributed refunds and unblocking
* [Setting up the integration](/en/midaz/plugins/pix/indirect/indirect-pix-integration) — ISPB and worker configuration
* [API reference](/en/reference/midaz/plugins/indirect-pix/create-entry) — Full API documentation
