RECEIVE
Overview
The RECEIVE webhook is sent when a PIX is received in your account. This event indicates that someone paid a QR Code generated by your application or made a direct transfer to your PIX key.
When it is sent
- QR Code (charge) payment confirmed
- Direct transfer to the account's PIX key
Payload Structure
{
"type": "RECEIVE",
"data": {
"id": 123,
"txId": "7978c0c97ea847e78e8849634473c1f1",
"pixKey": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906",
"status": "LIQUIDATED",
"payment": {
"amount": "100.00",
"currency": "BRL"
},
"refunds": [],
"createdAt": "2024-01-15T10:30:00.000Z",
"errorCode": null,
"endToEndId": "E12345678901234567890123456789012",
"ticketData": {},
"webhookType": "RECEIVE",
"debtorAccount": {
"ispb": "18236120",
"name": "NU PAGAMENTOS S.A.",
"issuer": "260",
"number": "12345-6",
"document": "123.xxx.xxx-xx",
"accountType": null
},
"idempotencyKey": null,
"creditDebitType": "CREDIT",
"creditorAccount": {
"ispb": null,
"name": null,
"issuer": null,
"number": null,
"document": null,
"accountType": null
},
"localInstrument": "DICT",
"transactionType": "PIX",
"remittanceInformation": "Pagamento pedido #12345"
}
}Important Fields
typestringAlways "RECEIVE" for received PIX.
data.idnumberTransaction ID. Use for idempotency.
data.txIdstringCharge identifier (txid from the /cob endpoint). Can be null for direct transfers.
data.endToEndIdstringEnd to End ID - unique identifier of the PIX transaction at the Central Bank.
data.statusstringTransaction status:
LIQUIDATED: Payment confirmed (success)ERROR: Processing failure
data.paymentobjectdata.debtorAccountobjectData of who paid (the payer/sender).
data.creditDebitTypestringAlways "CREDIT" for receipts.
data.refundsarrayList of refunds. Empty for transactions without refunds.
data.remittanceInformationstringTransfer description (if provided by the payer).
Processing the Webhook
Node.js Example
interface ReceiveWebhook {
type: 'RECEIVE';
data: {
id: number;
txId: string | null;
status: 'LIQUIDATED' | 'ERROR';
payment: {
amount: string;
currency: string;
};
endToEndId: string;
debtorAccount: {
name: string | null;
document: string | null;
};
remittanceInformation: string | null;
};
}
async function handleReceive(webhook: ReceiveWebhook) {
const { data } = webhook;
if (data.status !== 'LIQUIDATED') {
console.log(`PIX not confirmed: ${data.status}`);
return;
}
// Convert value from string to number
const amount = parseFloat(data.payment.amount);
// Find order by txId (if it is a charge)
if (data.txId) {
const order = await findOrderByTxId(data.txId);
if (order) {
await markOrderAsPaid(order.id, {
amount,
endToEndId: data.endToEndId,
payer: data.debtorAccount.name,
});
return;
}
}
// Receipt without an associated charge
await createGenericCredit({
amount,
endToEndId: data.endToEndId,
payer: data.debtorAccount.name,
description: data.remittanceInformation,
});
}Python Example
from decimal import Decimal
def handle_receive(webhook: dict):
data = webhook['data']
if data['status'] != 'LIQUIDATED':
print(f"PIX not confirmed: {data['status']}")
return
# Convert value
amount = Decimal(data['payment']['amount'])
# Process by txId if it exists
if data.get('txId'):
order = find_order_by_txid(data['txId'])
if order:
mark_order_as_paid(
order_id=order.id,
amount=amount,
e2e_id=data['endToEndId'],
payer=data['debtorAccount'].get('name')
)
return
# Generic credit
create_generic_credit(
amount=amount,
e2e_id=data['endToEndId'],
payer=data['debtorAccount'].get('name'),
description=data.get('remittanceInformation')
)Correlation with Charge
If the PIX was paid via a QR Code generated by the /cob/:txid endpoint, the txId field will contain the identifier:
{
"type": "RECEIVE",
"data": {
"txId": "7978c0c97ea847e78e8849634473c1f1",
// ...
}
}Use this field to correlate with your internal records:
// Create charge
const charge = await createCob('my-txid-123', { valor: '100.00' });
// Save association
await saveOrder({
orderId: 'order-456',
txId: 'my-txid-123',
status: 'PENDING'
});
// In the RECEIVE webhook
if (webhook.data.txId === 'my-txid-123') {
await updateOrder('order-456', { status: 'PAID' });
}Error Handling
If status === 'ERROR', check the errorCode field:
if (data.status === 'ERROR') {
console.error(`PIX error: ${data.errorCode}`);
// Notify about failure
await notifyPaymentError({
txId: data.txId,
errorCode: data.errorCode,
});
}Idempotency
Use data.id to avoid duplicate processing:
const PROCESSED_KEY = 'processed_webhooks';
async function handleWebhook(webhook: ReceiveWebhook) {
const webhookId = `receive:${webhook.data.id}`;
// Check if already processed
const isProcessed = await redis.sismember(PROCESSED_KEY, webhookId);
if (isProcessed) {
console.log(`Webhook ${webhookId} already processed`);
return;
}
// Mark as processed BEFORE processing
await redis.sadd(PROCESSED_KEY, webhookId);
// Process
await handleReceive(webhook);
}