Skip to main content
P2P (peer-to-peer) transfers are operations between accounts that belong to the same ISPB. Since they don’t involve communication with SPB, they are processed instantly and with lower operational cost.

When to use P2P


The plugin automatically detects when a transfer can be processed as P2P:
ScenarioTransfer type
Recipient’s ISPB different from yoursTED OUT (via SPB)
Recipient’s ISPB same as yoursP2P (internal)
Detection is transparent to the API client — the same endpoint is used for both cases.

P2P advantages


  • Speed: Settlement in less than 2 seconds (compared to minutes for TED).
  • Cost: No JD SPB fees, reduced Lerian fees.
  • Availability: Works 24/7, without depending on BACEN hours.

Comparison with TED OUT

AspectTED OUTP2P
Settlement time5-10 minutes< 2 seconds
Operating hoursMon-Fri 06:30-17:0024/7
Operational costInvolves JD SPBMidaz only
External dependenciesJD Consultores, BACENNone

How it works


The P2P flow is simplified:
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Initiation    │────▶│  P2P Detection  │────▶│   Recipient     │
│   (initiate)    │     │  (same ISPB)    │     │   validation    │
└─────────────────┘     └─────────────────┘     └────────┬────────┘

┌─────────────────┐     ┌─────────────────┐              │
│   Completed     │◀────│   Atomic Midaz  │◀─────────────┘
│   (completed)   │     │   transaction   │
└─────────────────┘     └─────────────────┘
  1. Initiation: client calls /v1/transfers/initiate
  2. Detection: plugin compares recipient’s ISPB with organization’s ISPB
  3. Validation: recipient is validated in CRM
  4. Confirmation: client calls /v1/transfers/process
  5. Execution: an atomic transaction is created in Midaz (debit + credit)
  6. Completion: transfer completes immediately

Timeline


T+0:     POST /v1/transfers/process
         ├─ Check ISPB (same = P2P)
         ├─ Validate recipient in CRM
         └─ Validate sender balance

T+1s:    Create atomic transaction in Midaz
         ├─ Debit from sender account
         └─ Credit to recipient account

T+2s:    Transfer completed
         └─ Status: COMPLETED
SLA target: < 2 seconds (typical; subject to load and availability) (vs TED OUT < 10 minutes)

Initiating a P2P transfer


The process is identical to TED OUT. The plugin automatically detects it’s P2P.

Step 1: Initiate

POST /v1/transfers/initiate
{
  "senderAccountId": "550e8400-e29b-41d4-a716-446655440000",
  "recipient": {
    "ispb": "12345678",
    "branch": "0001",
    "account": "987654",
    "accountType": "CACC",
    "holderName": "Ana Costa",
    "holderDocument": "98765432100"
  },
  "amount": 500.00,
  "description": "Internal transfer"
}

Response

{
  "initiationId": "e5f6a7b8-c9d0-1234-efgh-567890123456",
  "feeAmount": 0.00,
  "totalAmount": 500.00,
  "estimatedCompletionAt": "2026-01-21T22:00:02-03:00",
  "expiresAt": "2026-01-22T22:00:00-03:00",
  "status": "PENDING_CONFIRMATION"
}
Note that estimatedCompletionAt is practically immediate (2 seconds), indicating it will be processed as P2P.

Step 2: Confirm

POST /v1/transfers/process
{
  "initiationId": "e5f6a7b8-c9d0-1234-efgh-567890123456"
}

Response

{
  "transferId": "f6a7b8c9-d0e1-2345-fghi-678901234567",
  "confirmationNumber": "P2P20260121001",
  "status": "COMPLETED",
  "feeAmount": 0.00,
  "totalAmount": 500.00
}
Status already returns as COMPLETED — the transfer is instant.

P2P states


The state cycle is simpler than TED OUT:
CREATED → PROCESSING → COMPLETED
                    → FAILED
       → CANCELLED
StateDescription
CREATEDTransfer created
PROCESSINGExecuting transaction in Midaz
COMPLETEDTransfer completed
FAILEDMidaz error (rare)
CANCELLEDCancelled before processing
In practice, the transition from CREATED to COMPLETED occurs in less than 2 seconds.

Query P2P transfer


GET /v1/transfers/{transferId}
{
  "transferId": "f6a7b8c9-d0e1-2345-fghi-678901234567",
  "type": "P2P",
  "status": "COMPLETED",
  "sender": {
    "accountId": "550e8400-e29b-41d4-a716-446655440000",
    "holderName": "João Santos"
  },
  "recipient": {
    "accountId": "660e8400-e29b-41d4-a716-446655440001",
    "ispb": "12345678",
    "branch": "0001",
    "account": "987654",
    "accountType": "CACC",
    "holderName": "Ana Costa",
    "holderDocument": "98765432100"
  },
  "amount": 500.00,
  "feeAmount": 0.00,
  "totalAmount": 500.00,
  "confirmationNumber": "P2P20260121001",
  "createdAt": "2026-01-21T22:00:00-03:00",
  "completedAt": "2026-01-21T22:00:01-03:00",
  "statusHistory": [
    {"status": "CREATED", "timestamp": "2026-01-21T22:00:00-03:00"},
    {"status": "PROCESSING", "timestamp": "2026-01-21T22:00:00-03:00"},
    {"status": "COMPLETED", "timestamp": "2026-01-21T22:00:01-03:00"}
  ]
}
The type field is P2P and there’s no controlNumber (as it didn’t go through JD SPB).

P2P fees


Fees for P2P transfers are configured separately from TED fees:
ConfigurationDescription
p2p_fee_enabledEnables fee charging for P2P
p2p_fee_typeFee type (flat, percentage, tiered)
It’s common for institutions not to charge fees on internal transfers, but the configuration is available.

24/7 operation


Unlike TED, P2P transfers can be made at any time:
  • Business days: works normally
  • Weekends: works normally
  • Holidays: works normally
  • Late night: works normally
There’s no operating hours validation for P2P, as the operation doesn’t depend on SPB.

Recipient validation


The plugin validates the recipient in two ways:

By bank details

If the recipient is provided with ISPB, branch, and account:
  1. Checks if ISPB matches the organization’s
  2. Searches for the account in CRM by bank details
  3. Validates if the document matches

By accountId (optimized)

If you already know the recipient’s Midaz accountId, you can use more direct validation through CRM.

Error handling


Recipient not found (404)

{
  "code": "BTF-0500",
  "title": "Account Not Found",
  "message": "Recipient account not found in CRM.",
  "fields": {
    "ispb": "12345678",
    "branch": "0001",
    "account": "987654"
  }
}

Insufficient balance (422)

{
  "code": "BTF-2001",
  "title": "Insufficient Balance",
  "message": "Sender account does not have sufficient balance.",
  "fields": {
    "available": 300.00,
    "required": 500.00
  }
}

Same source and destination account (400)

{
  "code": "BTF-0001",
  "title": "Invalid Input",
  "message": "Sender and recipient cannot be the same account."
}

Common use cases


CaseDescription
Transfer between usersClient A transfers to Client B
Movement between own accountsClient moves between checking and savings
Internal paymentCompany pays employee at same institution
Payment splitDistribution of amounts across multiple accounts

Atomicity


P2P transfers are atomic in Midaz:
  • Debit and credit occur in the same transaction
  • If any part fails, the entire operation is reverted
  • There’s no intermediate state where money “disappears”
This ensures consistency even in failure scenarios.
Reduced partial-failure risk: The atomic Midaz transaction ensures that debit and credit either both succeed or both fail, preventing partial transfers. However, duplicate transfer submissions (e.g., retries or double-clicks) still require idempotency controls — always use the X-Idempotency-Key header as described in Best practices.