Brazil Pix Gate
Accept Pix pay-ins on a hosted QR page and send Pix payouts to a CPF key in Brazilian Real (BRL).
When to use it
The Brazil Pix Gate accepts Pix pay-ins on a MagicPayments-hosted page — the payer
enters their identification (name, CPF, e-mail, phone), then the page shows a Pix QR code and
the "Pix Copia e Cola" string; the payer completes the transfer in their banking app — and
sends Pix payouts to a CPF Pix key. All amounts are in BRL
(Brazilian Real, 2 decimal places — so 10000 means 100.00 BRL). Individuals only:
payers and payout recipients are identified by CPF (CNPJ is not supported).
| Operation | Rail | How the integration looks |
|---|---|---|
| Pay-in | Pix (QR / Copia e Cola) | Create an invoice, redirect the payer to the hosted page. |
| Pay-out | Pix key (CPF) | Server-to-server call to the wallet-account payout endpoint. |
Pay-ins and payouts accept 50 – 5,000 BRL per operation. In minor units that is
5000–500000.
You need your signing credentials from Getting started, a
pay-in gate_id for Brazil Pix, and a pay-out
cascade_id enabled for BRL Pix payouts. Your account manager provides both.
First-time and repeat depositors may be routed through separate gates — if so, your account manager
gives you two gate_ids and tells you which is which.
Pay-in — hosted Pix QR page
The payer never touches your servers with their payment details. You create an invoice; we host the page.
-
Create the invoice
Call the gate endpoint with your
gate_idand the amount in BRL minor units. -
Redirect the payer
Send the customer's browser to the
invoice_urlwe return. Our page collects the payer's name, CPF, e-mail and phone, then shows the Pix QR code and the "Copia e Cola" string with a countdown; the payer completes the transfer in their banking app. -
Receive the result
The transfer is matched automatically — the payer does not confirm anything on the page. We call
your
callback_urlon every status change; you can poll invoice status as a fallback.
/api/invoice{
"gate_id": "br-pix-gate",
"invoice": {
"invoice_id": "order-br-2026-000123",
"currency": "BRL",
"amount": 10000,
"description": "Wallet top-up",
"ttl_minutes": 30
},
"customer": {
"id": "cust-88130",
"full_name": "Joao Silva",
"phone_number": "+5511999999999"
},
"workflow_hooks": {
"callback_url": "https://merchant.example.com/mp/callbacks",
"return_success_url": "https://merchant.example.com/orders/123/done",
"return_decline_url": "https://merchant.example.com/orders/123/retry"
}
}
Python
resp = signed_post("/api/invoice", {
"gate_id": "br-pix-gate",
"invoice": {
"invoice_id": "order-br-2026-000123",
"currency": "BRL",
"amount": 10_000, # 100.00 BRL (2 decimal places)
"description": "Wallet top-up",
"ttl_minutes": 30,
},
"customer": {
"id": "cust-88130",
"full_name": "Joao Silva",
"phone_number": "+5511999999999",
},
"workflow_hooks": {
"callback_url": "https://merchant.example.com/mp/callbacks",
"return_success_url": "https://merchant.example.com/orders/123/done",
"return_decline_url": "https://merchant.example.com/orders/123/retry",
},
})
invoice = resp.json()
redirect_url = invoice["invoice_url"] # send the payer here
Response
{
"request_id": "0f0b1d2e-...",
"request_status": "success",
"invoice_id": "62051f64-9a79-4edb-8a9e-95c86c55ee4e",
"merchant_invoice_id": "order-br-2026-000123",
"invoice_status": "unpaid",
"invoice_url": "https://stage.example-mp.com/public/invoice/62051f64-.../gate",
"message": "Invoice (Gate PayIn) with internal uid=62051f64-... has been created."
}
The Pix code our page shows is valid for a limited window (typically ~10 minutes) and the page
shows the payer a countdown. Set a ttl_minutes that gives the payer time to complete the
transfer in their banking app, and lean on the callback rather than tight polling.
The Pix transfer must come from an account linked to the CPF the payer entered on the page. A transfer from someone else's account may not be matched to the payment.
Tracking the pay-in
Look up the invoice by your own id (or our invoice_uid). It reaches paid on success.
/api/invoice/statusresp = signed_post("/api/invoice/status", {
"merchant_invoice_id": "order-br-2026-000123"
})
invoice = resp.json()
# invoice["status"] -> "paid" | "payment_failed" | "expired" | "canceled" | ...
# invoice["actual_payment"]["processing_info"]["amount_acquired"] -> settled BRL minor units
Pay-out — to a CPF Pix key
Payouts go server-to-server. The destination is the recipient's CPF used as their Pix
key (11 digits, no dots or dashes). Use the wallet-account payout endpoint with a
cascade_id enabled for BRL payouts.
/api/payment/payout/wallet_account{
"cascade_id": "br-pix-payout",
"payment": {
"payment_id": "payout-br-2026-000045",
"currency": "BRL",
"amount": 5000,
"description": "Affiliate payout"
},
"wallet": {
"provider": "pix",
"wallet_number": "12345678909",
"full_name": "Richard Roe"
},
"customer": {
"id": "partner-2391",
"full_name": "Richard Roe",
"phone_number": "+5511999999999"
},
"workflow_hooks": {
"callback_url": "https://merchant.example.com/mp/callbacks"
}
}
wallet.wallet_number must be the recipient's CPF (11 digits).
Phone, e-mail and random (EVP) Pix keys are not supported on this rail, and payouts to legal
entities (CNPJ) are not accepted. wallet.full_name is the recipient's name and is
required.
resp = signed_post("/api/payment/payout/wallet_account", {
"cascade_id": "br-pix-payout",
"payment": {
"payment_id": "payout-br-2026-000045",
"currency": "BRL",
"amount": 5_000, # 50.00 BRL — the payout minimum
"description": "Affiliate payout",
},
"wallet": {
"provider": "pix",
"wallet_number": "12345678909", # recipient CPF, digits only
"full_name": "Richard Roe",
},
"customer": {
"id": "partner-2391",
"full_name": "Richard Roe",
"phone_number": "+5511999999999",
},
"workflow_hooks": {"callback_url": "https://merchant.example.com/mp/callbacks"},
})
payout = resp.json()
payout_uid = payout["payment_id"] # our uid; status starts at "initiated"
Tracking the pay-out
Payouts use the payment status endpoint (not the invoice one). The reason for a rejected payout is
delivered with the decline status in your callback.
/api/payment/statusresp = signed_post("/api/payment/status", {
"merchant_payment_id": "payout-br-2026-000045"
})
# resp.json()["status"] -> "success" | "processing" | "decline" | "error"
Testing & go-live
- On stage, use the test
gate_id/cascade_idfrom your account manager; no real funds move. - Verify you can drive a pay-in to
paidand a payout tosuccess, and that your callback handler verifies the signature and is idempotent. - Confirm your BRL amount conversion:
amountis minor units with 2 decimal places. - Stay inside the limits: 50–5,000 BRL per operation for both directions.
- Validate CPFs before submitting payouts (11 digits, checksum) — an invalid or non-Pix-enabled CPF is the most common cause of a
decline. - Swap base URL and credentials to production; the request shapes are identical.