Deposits (Pix-In)
A deposit is an immediate charge: you create a PIX QR Code, the player pays it from any bank app, and LB Pay notifies you so you can credit the wallet.
API: POST /v1/pix/deposit/immediate-charge,
GET /v1/pix/deposit/immediate-charge/{chargeId}
The flow
Creating the charge
curl --request POST \
--url "https://api.sdb.lbpay.com.br/v1/pix/deposit/immediate-charge" \
--header "Authorization: Bearer $TOKEN" \
--header 'content-type: application/json' \
--data '{
"externalId": "3f2b1c4e-9a7d-4e21-b8f0-5c6d7e8a9b01",
"amount": 5000,
"expiresIn": 600,
"pixKey": "YOUR_OPERATOR_PIX_KEY",
"expectedPayer": { "taxId": "12345678909" }
}'
amount is an integer in cents — 5000 is R$ 50,00. pixKey is the destination key,
yours, identifying the account being credited.
A 201 response carries the chargeId, the copy-and-paste payload in brCode, and status
PENDING.
Expiration must match your interface
expiresIn is the QR Code lifetime in seconds. The most common deposit bug is a
mismatch between this value and the countdown shown to the player.
If the QR Code expires in 300 s but your interface shows a 10-minute countdown, players who pay at minute 7 are rejected — and they experience it as your platform taking their money and failing.
Use at least 600 seconds, and drive the interface countdown from expiresAt returned by
the API rather than from a hardcoded constant.
Send your own identifier
Put your internal transaction or player identifier in externalId, and anything else you
need in metadata. Both come back on the statement, so reconciliation becomes a join
instead of a guess — see Reconciliation.
Confirming the payment
pixChargePaidNever credit a wallet because a QR Code was displayed, because the player says they paid, or
because a screen was reached. The pixChargePaid webhook — or a GET returning PAID — is
the only authorisation to move balance.
If no confirmation arrives within 60 seconds, query the charge directly. Webhooks can be lost; see Contingency polling.
Charges that are not paid
A charge stays PENDING until it is paid or reaches expiresAt. Two rules follow:
- Do not cancel and recreate a charge without checking its current status. A player may pay a QR Code you already considered abandoned.
- A failed payment attempt does not cancel the QR Code. The player can try again against
the same charge until it expires — so a single charge may see several attempts before one
settles. Design the credit path around "exactly one
pixChargePaidper charge", not around "one attempt per charge".
An error webhook is not the end of the charge
When an attempt fails you receive pixChargeRejected. Look at what it actually carries:
{
"event": "pixChargeRejected",
"status": "pending",
"rejectedReason": "ACCOUNT_MISMATCH"
}
The status is pending, not a terminal value. The event reports a rejected attempt,
not a closed charge.
Your system must accept pixChargeRejected without treating it as an outcome, keep the
charge open, and keep waiting for the real ending — pixChargePaid or pixChargeExpired.
Closing it here discards a deposit the player is likely to retry, and the payment then arrives with nothing on your side expecting it.
Optional payer checks
expectedPayer lets you declare, when creating the charge, who you expect to pay it. LB Pay
then validates the incoming payment against what you declared. Both checks are optional —
you enable them by sending the corresponding field.
| Check | Field | What it verifies |
|---|---|---|
| Same payer | expectedPayer.taxId | The CPF that paid the QR Code is the same one that generated it |
| Same bank account | expectedPayer.bankAccounts | The account used to pay is one of the accounts you sent in the payload |
{
"expectedPayer": {
"name": "Player Full Name",
"taxId": "12345678909",
"bankAccounts": [
{
"bankCode": "12345678",
"branchCode": "0001",
"accountNumber": "1234567",
"accountType": "CACC",
"personType": "NATURAL_PERSON"
}
]
}
}
Send only taxId to check the payer, only bankAccounts to check the account, or both.
Omit expectedPayer entirely and no payer validation is applied.
If the player is logged in, you already know their CPF and their registered accounts. Declaring them costs nothing and turns a reconciliation problem into a rejection at payment time, which is far cheaper to handle.
CPF Shield — third-party payments
CPF Shield is an optional flag. When enabled, it blocks a deposit paid from a CPF different from the one that generated the charge.
It exists because third-party payment is a real scenario in this market — a legal guardian or a family member may pay for the player — and regulation requires the operator to decide whether that is acceptable.
| Payer CPF | Outcome |
|---|---|
| Same as the player's | Accepted, wallet credited |
| Different, and allowed by your configuration | Accepted, wallet credited |
| Different, and not allowed | Refused at settlement — the payment does not go through |
The block happens at settlement: the money never leaves the payer's account, so there is
nothing to credit and nothing to undo. You receive pixChargeRejected, and the charge stays
payable until expiresAt — see Charges that are not paid.
You declare the expected CPF in expectedPayer.taxId when creating the charge. On
withdrawals the equivalent rule is stricter and has no shield: a destination CPF that
differs from the player's is rejected with 422 IGAMING_CPF_MISMATCH — see
Withdrawals.
Testing
In staging, simulate a player paying an open charge:
curl --request POST \
--url "https://api.sdb.lbpay.com.br/v1/pix/mock/simulate-payment" \
--header "Authorization: Bearer $TOKEN" \
--header 'content-type: application/json' \
--data '{
"chargeId": "THE_CHARGE_ID",
"receiverCnpj": "YOUR_RECEIVING_CNPJ"
}'
Both fields are required. The amount and payer details are recovered from the charge.
Related
- Status Machine — Pix In — every status and transition
- Pix In errors — error codes for this route
- Refunds — returning a confirmed deposit
- Webhooks — deposit event payloads