Boleto Flow
This guide describes the complete lifecycle of a boleto on the FluxiQ NPC platform, from creation to final settlement.
Overview
The diagram below illustrates the states of a boleto throughout its lifecycle:
+-----------------------------------------------------------------------------+
| Boleto Lifecycle |
+-----------------------------------------------------------------------------+
+---------+ +------------+ +--------+ +----------+
| DRAFT |----->| REGISTERED |----->| PAID |----->| SETTLED |
+---------+ +------------+ +--------+ +----------+
| |
| |
v v
+-----------------------+
| CANCELLED |
+-----------------------+
Status:
- DRAFT : Boleto created, awaiting registration
- REGISTERED : Registered with Nuclea (PCR)
- PAID : Payment confirmed
- SETTLED : Settled (funds credited)
- CANCELLED : CancelledStep 1: Create Boleto
The first step is to create the boleto via API. The boleto will be automatically registered with Nuclea (PCR).
curl -X POST "https://api.pixconnect.com.br/api/v1/central/boletos" \
-H "X-API-Key: pk_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"boleto": {
"nosso_numero": "12345678901",
"amount_cents": 15000,
"due_date": "2026-03-15",
"payer_document": "12345678901",
"payer_name": "Joao Silva Santos",
"beneficiary_ispb": "02992335",
"beneficiary_name": "Empresa XYZ Ltda"
}
}'async function createBoleto() {
const response = await fetch(
"https://api.pixconnect.com.br/api/v1/central/boletos",
{
method: "POST",
headers: {
"X-API-Key": "pk_live_abc123def456",
"Content-Type": "application/json",
},
body: JSON.stringify({
boleto: {
nosso_numero: "12345678901",
amount_cents: 15000,
due_date: "2026-03-15",
payer_document: "12345678901",
payer_name: "Joao Silva Santos",
beneficiary_ispb: "02992335",
beneficiary_name: "Empresa XYZ Ltda"
}
}),
}
);
const data = await response.json();
if (data.success) {
console.log("Boleto created:", data.data.nosso_numero);
console.log("Barcode:", data.data.barcode);
console.log("Typeable line:", data.data.linha_digitavel);
}
return data;
}import requests
def create_boleto():
url = "https://api.pixconnect.com.br/api/v1/central/boletos"
headers = {
"X-API-Key": "pk_live_abc123def456",
"Content-Type": "application/json"
}
payload = {
"boleto": {
"nosso_numero": "12345678901",
"amount_cents": 15000,
"due_date": "2026-03-15",
"payer_document": "12345678901",
"payer_name": "Joao Silva Santos",
"beneficiary_ispb": "02992335",
"beneficiary_name": "Empresa XYZ Ltda"
}
}
response = requests.post(url, headers=headers, json=payload)
data = response.json()
if data.get("success"):
print(f"Boleto created: {data['data']['nosso_numero']}")
print(f"Barcode: {data['data']['barcode']}")
print(f"Typeable line: {data['data']['linha_digitavel']}")
return dataExpected response:
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"nosso_numero": "12345678901",
"barcode": "23793381286000000000000000012345678901500001",
"linha_digitavel": "23793.38128 60000.000003 00001.234567 8 90150000015000",
"amount_cents": 15000,
"due_date": "2026-03-15",
"status": "registered",
"pcr_protocol": "PCR2026021512345678"
}
}Step 2: Wait for Registration
After creation, the boleto is automatically registered with Nuclea. You can track this in two ways:
Option A: Webhook (Recommended)
Configure an endpoint to receive the boleto_registered event:
// Your server (Express.js example)
app.post("/webhooks/pixconnect", async (req, res) => {
const { event, data } = req.body;
// Validate signature first (see Webhooks documentation)
if (!validateSignature(req)) {
return res.status(401).json({ error: "Invalid signature" });
}
if (event === "boleto_registered") {
console.log("Boleto registered:", data.nosso_numero);
console.log("PCR Protocol:", data.pcr_protocol);
// Update your database
await updateBoletoStatus(data.nosso_numero, "registered", {
pcr_protocol: data.pcr_protocol,
registered_at: data.registered_at
});
}
res.status(200).json({ received: true });
});Option B: Polling
If you cannot use webhooks, query the status periodically:
async function waitForRegistration(nossoNumero, maxAttempts = 10) {
for (let i = 0; i < maxAttempts; i++) {
const response = await fetch(
`https://api.pixconnect.com.br/api/v1/central/boletos/${nossoNumero}`,
{
headers: {
"X-API-Key": "pk_live_abc123def456",
"Content-Type": "application/json",
},
}
);
const data = await response.json();
if (data.data.status === "registered") {
console.log("Boleto registered successfully!");
return data.data;
}
// Wait 2 seconds before next attempt
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error("Timeout waiting for boleto registration");
}Polling Usage
Polling consumes more API resources. Use webhooks whenever possible. If using polling, respect the rate limits.
Step 3: Display Boleto to Customer
With the boleto registered, display the payment information to the customer:
function displayBoleto(boleto) {
return {
// Barcode (44 digits - for scanner reading)
barcode: boleto.barcode,
// Typeable line (47 formatted digits - for manual typing)
linhaDigitavel: boleto.linha_digitavel,
// Formatted value
valor: (boleto.amount_cents / 100).toLocaleString("pt-BR", {
style: "currency",
currency: "BRL"
}),
// Formatted due date
vencimento: new Date(boleto.due_date).toLocaleDateString("pt-BR"),
// Beneficiary
beneficiario: boleto.beneficiary_name
};
}
// Usage example
const boletoDisplay = displayBoleto(boleto);
// {
// barcode: "23793381286000000000000000012345678901500001",
// linhaDigitavel: "23793.38128 60000.000003 00001.234567 8 90150000015000",
// valor: "R$ 150.00",
// vencimento: "15/03/2026",
// beneficiario: "Empresa XYZ Ltda"
// }PDF Generation
To generate a boleto PDF, include the following elements:
- Barcode: Use a barcode library to generate the image
- Typeable line: Display legibly for typing
- Beneficiary data: Name, CNPJ/CPF, address
- Payer data: Name, CPF/CNPJ
- Value and due date: Clearly visible
- Instructions: Information for the teller
Step 4: Receive Payment Notification
When the customer pays the boleto, you will receive the boleto_paid webhook:
app.post("/webhooks/pixconnect", async (req, res) => {
const { event, data } = req.body;
if (!validateSignature(req)) {
return res.status(401).json({ error: "Invalid signature" });
}
switch (event) {
case "boleto_paid":
console.log("Payment received!");
console.log("Nosso numero:", data.nosso_numero);
console.log("Amount paid:", data.valor_pago / 100);
console.log("Payment date:", data.data_pagamento);
// Update status in your system
await updateBoletoStatus(data.nosso_numero, "paid", {
valor_pago: data.valor_pago,
data_pagamento: data.data_pagamento,
canal_pagamento: data.canal_pagamento,
settlement_id: data.settlement_id
});
// Release service/product to customer
await fulfillOrder(data.nosso_numero);
// Send confirmation email
await sendPaymentConfirmation(data.nosso_numero);
break;
// ... other events
}
res.status(200).json({ received: true });
});boleto_paid event payload:
{
"event": "boleto_paid",
"timestamp": "2026-02-04T09:15:00Z",
"data": {
"nosso_numero": "12345678901",
"valor_original": 15000,
"valor_pago": 15000,
"valor_desconto": 0,
"valor_juros": 0,
"valor_multa": 0,
"data_pagamento": "2026-02-04",
"data_credito": "2026-02-05",
"canal_pagamento": "internet_banking",
"settlement_id": "stl_abc123def456"
}
}Step 5: Settlement Reconciliation
The cycle completes with settlement, when funds are effectively credited:
app.post("/webhooks/pixconnect", async (req, res) => {
const { event, data } = req.body;
if (!validateSignature(req)) {
return res.status(401).json({ error: "Invalid signature" });
}
switch (event) {
case "settlement_completed":
console.log("Settlement cycle completed!");
console.log("ID:", data.settlement_id);
console.log("Total boletos:", data.total_boletos);
console.log("Total value:", data.total_valor / 100);
// Update accounting
await updateAccountingRecords(data);
// Generate cycle report
await generateSettlementReport(data);
break;
case "payment_received":
// Individual confirmation of each payment in the cycle
console.log("Payment settled:", data.nosso_numero);
console.log("Value:", data.valor_pago / 100);
// Mark as settled
await updateBoletoStatus(data.nosso_numero, "settled", {
data_credito: data.data_credito
});
break;
}
res.status(200).json({ received: true });
});Error Handling
Registration Failure
If the boleto fails to register with Nuclea, you may receive an error event:
app.post("/webhooks/pixconnect", async (req, res) => {
const { event, data } = req.body;
if (event === "boleto_registration_failed") {
console.error("Registration failure:", data.nosso_numero);
console.error("Reason:", data.error_code, data.error_message);
// Notify support team
await alertSupport({
type: "registration_failed",
boleto: data.nosso_numero,
error: data.error_message
});
// Retry or cancel
if (isRetryable(data.error_code)) {
await retryRegistration(data.nosso_numero);
} else {
await cancelBoleto(data.nosso_numero);
}
}
res.status(200).json({ received: true });
});
function isRetryable(errorCode) {
const retryableCodes = ["TIMEOUT", "SERVICE_UNAVAILABLE", "RATE_LIMITED"];
return retryableCodes.includes(errorCode);
}Expired Boleto
Handle boletos that expired without payment:
// Daily check for expired boletos
async function checkExpiredBoletos() {
const today = new Date().toISOString().split("T")[0];
const response = await fetch(
`https://api.pixconnect.com.br/api/v1/central/boletos?status=registered&due_date_lt=${today}`,
{
headers: {
"X-API-Key": "pk_live_abc123def456",
"Content-Type": "application/json",
},
}
);
const { data: boletos } = await response.json();
for (const boleto of boletos) {
console.log("Expired boleto:", boleto.nosso_numero);
// Check business policy
const daysExpired = daysSince(boleto.due_date);
if (daysExpired > 30) {
// Cancel after 30 days
await cancelBoleto(boleto.nosso_numero);
await notifyCustomerCancelled(boleto);
} else if (daysExpired > 7) {
// Reminder after 7 days
await sendPaymentReminder(boleto);
}
}
}
function daysSince(dateString) {
const date = new Date(dateString);
const now = new Date();
return Math.floor((now - date) / (1000 * 60 * 60 * 24));
}Sequence Diagram
+--------+ +------------+ +-----------+ +--------+
| Client | | Your API | | FluxiQ NPC| | Nuclea |
+---+----+ +-----+------+ +-----+-----+ +---+----+
| | | |
| 1. Request boleto | | |
|------------------->| | |
| | | |
| | 2. POST /boletos | |
| |---------------------->| |
| | | |
| | | 3. Register PCR |
| | |------------------>|
| | | |
| | | 4. Confirmation |
| | |<------------------|
| | | |
| | 5. 201 Created | |
| |<----------------------| |
| | (barcode, line) | |
| | | |
| 6. Display boleto | | |
|<--------------------| | |
| | | |
| 7. Pay boleto | | |
|-------------------------------------------------------------------->
| | | |
| | | 8. Payment |
| | |<------------------|
| | | |
| | 9. Webhook | |
| | boleto_paid | |
| |<----------------------| |
| | | |
| 10. Confirm | | |
| payment | | |
|<--------------------| | |
| | | |
| | 11. Webhook | |
| | settlement_ | |
| | completed | |
| |<----------------------| |
| | | |
+---+----+ +-----+------+ +-----+-----+ +---+----+Integration Checklist
Use this checklist to validate your integration:
Boleto Creation
- [ ] Generate unique
nosso_numero(11 digits) - [ ] Send value in cents (
amount_cents) - [ ] Format due date (YYYY-MM-DD)
- [ ] Validate payer CPF/CNPJ
- [ ] Configure beneficiary ISPB correctly
Webhooks
- [ ] HTTPS endpoint configured
- [ ] HMAC signature validation implemented
- [ ] Handler for
boleto_registered - [ ] Handler for
boleto_paid - [ ] Handler for
settlement_completed - [ ] Return 200 OK within 30 seconds
Display
- [ ] Readable barcode (scanner)
- [ ] Correctly formatted typeable line
- [ ] Visible value and due date
- [ ] Beneficiary data displayed
Error Handling
- [ ] Handler for registration failures
- [ ] Check for expired boletos
- [ ] Logs for debugging
- [ ] Alerts for support team
Reconciliation
- [ ] Status tracking
- [ ] Payment records
- [ ] Accounting integration
- [ ] Settlement reports
Next Steps
- Settlement Flow - Understand the settlement process
- Webhook Events - Complete event reference
- Boletos API - API reference