How Renegotiation Works
Overview
Renegotiation allows customers to modify the terms of an existing financed billing by creating a new payment plan with different installment amounts, due dates, or payment schedules. This feature is essential for managing customer payment difficulties and maintaining healthy business relationships.
This guide explains the technical process of renegotiation, webhook events, and how to properly integrate this functionality into your system.
What is Renegotiation?
Renegotiation is the process of modifying an active billing by:
- Creating a new billing with updated payment terms
- Canceling the original billing
- Maintaining the same order (order status remains unchanged)
Common use cases for renegotiation:
- Customer requests extended payment terms
- Customer needs to adjust installment amounts
- Customer requires different due dates
- Payment restructuring scenarios
How the Renegotiation Process Works
Step-by-Step Flow
When a renegotiation is initiated, the following sequence occurs:
1. Order Status Remains Unchanged
The order status stays as finished. Renegotiation does not affect the order level - it only impacts the billing level.
Example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "finished", // Remains "finished" after renegotiation
"billings": [
// Will contain both old (canceled) and new (waiting_payment) billings
]
}
For more details, see the Order schema documentation.
2. Original Billing is Canceled
The existing billing is canceled with a specific cancelation reason: customer_renegotiation.
Webhook Event Triggered:
Event: billing.canceled
Entity: billing
Cancelation Reason: customer_renegotiation
Canceled Billing Example:
{
"id": "a1b2c3d4-e5f6-4789-0abc-def123456789",
"status": "canceled",
"cancelation_reason": "customer_renegotiation",
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"amount_cents": 299700,
"installments": 24,
"bills": [
{
"id": "bill-001",
"status": "paid",
"amount_cents": 12488
},
{
"id": "bill-002",
"status": "canceled",
"amount_cents": 12488
}
]
}
For more details about billing cancelation reasons, see the Billing schema documentation.
3. New Billing is Created
A new billing is created immediately with the updated payment terms. The new billing starts with status waiting_payment.
New Billing Properties:
- Status:
waiting_payment(transitions toactivewhen first bill is paid) - origin_billing_id: References the original billing ID
- New payment terms: Updated installment amounts, due dates, or payment schedule
New Billing Example:
{
"id": "b2c3d4e5-f6a7-8901-2bcd-ef0123456789",
"status": "waiting_payment",
"origin_billing_id": "a1b2c3d4-e5f6-4789-0abc-def123456789",
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"amount_cents": 287212,
"installments": 18,
"due_day": 15,
"bills": [
{
"id": "bill-101",
"status": "pending",
"amount_cents": 15956,
"due_date": "2024-04-15T23:59:59.999-03:00"
},
{
"id": "bill-102",
"status": "pending",
"amount_cents": 15956,
"due_date": "2024-05-15T23:59:59.999-03:00"
}
]
}
4. Order Now Contains Both Billings
After renegotiation, the order will have:
- 1 canceled billing (the original billing with
cancelation_reason: customer_renegotiation) - 1 waiting_payment billing (the new billing with updated terms)
Complete Order Example After Renegotiation:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "finished",
"customer_name": "João Silva",
"customer_document": "12345678901",
"customer_type": "person",
"billings": [
{
"id": "a1b2c3d4-e5f6-4789-0abc-def123456789",
"status": "canceled",
"cancelation_reason": "customer_renegotiation",
"amount_cents": 299700,
"installments": 24,
"origin_billing_id": null
},
{
"id": "b2c3d4e5-f6a7-8901-2bcd-ef0123456789",
"status": "waiting_payment",
"cancelation_reason": null,
"amount_cents": 287212,
"installments": 18,
"origin_billing_id": "a1b2c3d4-e5f6-4789-0abc-def123456789"
}
]
}
Webhook Integration for Renegotiation
Important: No Order-Level Webhook
Renegotiation does NOT trigger an order webhook. The order status remains finished and no order event is fired.
Billing Webhook is Triggered
When a renegotiation occurs, you will receive a billing.canceled webhook with cancelation_reason: customer_renegotiation.
Setting Up the Webhook
To listen for renegotiation events, create a webhook for billing canceled events:
curl --location 'https://external-api-sandbox.lia.com.br/external_api/v1/webhooks' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--data '{
"name": "Billing Renegotiation Webhook",
"url": "https://your-domain.com/webhooks/lia/billing-renegotiation",
"entity": "billing",
"events": ["canceled"],
"verb": "post"
}'
For more information about webhooks, see the Webhooks documentation.
Webhook Payload Example
When a renegotiation happens, you will receive this payload:
{
"event": "billing.canceled",
"data": {
"id": "a1b2c3d4-e5f6-4789-0abc-def123456789",
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "canceled",
"cancelation_reason": "customer_renegotiation",
"amount_cents": 299700,
"amount_currency": "BRL",
"due_day": 10,
"installments": 24,
"origin_billing_id": null,
"finished_at": null,
"created_at": "2024-01-15T10:30:00.000-03:00",
"updated_at": "2024-03-20T14:45:00.000-03:00",
"payment_configuration": {
"id": "config-123",
"payment_type": "financed",
"financed_type": "bolepix"
},
"bills": [
{
"id": "bill-001",
"status": "paid",
"amount_cents": 12488
},
{
"id": "bill-002",
"status": "canceled",
"amount_cents": 12488
}
]
}
}
Identifying Renegotiation Events
To specifically identify renegotiation (vs other billing cancellations), check the cancelation_reason field:
// Example webhook handler
function handleBillingCanceled(webhookData) {
const billing = webhookData.data;
if (billing.cancelation_reason === 'customer_renegotiation') {
// This is a renegotiation event
handleRenegotiation(billing);
} else {
// This is another type of cancellation
handleOtherCancellation(billing);
}
}
function handleRenegotiation(billing) {
// 1. Fetch the new billing to get updated payment terms
const order = fetchOrder(billing.order_id);
const newBilling = order.billings.find(b =>
b.origin_billing_id === billing.id && b.status === 'waiting_payment'
);
// 2. Update your internal systems with the new billing information
updateInternalBilling(newBilling);
// 3. Notify customer about the renegotiation
notifyCustomer(billing.order_id, newBilling);
}
Renegotiation Constraints and Rules
Eligibility Requirements
Renegotiations can only be performed on billings that meet these criteria:
| Requirement | Description |
|---|---|
| Billing Status | Must be active (cannot renegotiate waiting_payment, finished, or canceled billings) |
| Payment Type | Must be a financed payment type (upfront payments cannot be renegotiated) |
| Original Amount | All installments must meet the minimum installment amount requirement |
Technical Limits
| Limit | Value | Description |
|---|---|---|
| Max Installments | 50 | Maximum number of installments allowed in a renegotiation |
| Max Start Date | 12 months | Maximum time in the future for the first due date |
| Min Installment Amount | 5,000 cents (R$ 50.00) | Minimum amount per installment |
Monitoring Renegotiations
Fetching Order with Renegotiation History
To see all billings (including canceled and renegotiated ones):
curl --location 'https://external-api-sandbox.lia.com.br/external_api/v1/orders/ORDER_ID' \
--header 'Authorization: Bearer YOUR_API_TOKEN'
Identifying Renegotiation Chains
You can track renegotiation chains using the origin_billing_id field:
function getRenegotiationHistory(order) {
const billings = order.billings;
const chains = [];
// Find root billings (no origin_billing_id)
const rootBillings = billings.filter(b => !b.origin_billing_id);
rootBillings.forEach(root => {
const chain = [root];
let current = root;
// Follow the chain of renegotiations
while (true) {
const next = billings.find(b => b.origin_billing_id === current.id);
if (!next) break;
chain.push(next);
current = next;
}
chains.push(chain);
});
return chains;
}
// Example usage
const order = await fetchOrder('ORDER_ID');
const renegotiationHistory = getRenegotiationHistory(order);
console.log(`Order has ${renegotiationHistory.length} billing chain(s)`);
renegotiationHistory.forEach((chain, index) => {
console.log(`Chain ${index + 1}:`);
chain.forEach(billing => {
console.log(` - ${billing.id} (${billing.status})`);
});
});
Fetching Billing Details
To get details of a specific billing:
curl --location 'https://external-api-sandbox.lia.com.br/external_api/v1/billings/BILLING_ID' \
--header 'Authorization: Bearer YOUR_API_TOKEN'
For more information, see the Billings API documentation.
Complete Integration Example
Here’s a complete example of integrating renegotiation handling:
// 1. Set up webhook endpoint
app.post('/webhooks/lia/billing', async (req, res) => {
const { event, data: billing } = req.body;
// Acknowledge receipt immediately
res.status(200).json({ received: true });
// Process webhook asynchronously
if (event === 'billing.canceled') {
await handleBillingCanceled(billing);
}
});
// 2. Handle billing canceled events
async function handleBillingCanceled(billing) {
console.log(`Billing ${billing.id} was canceled`);
console.log(`Reason: ${billing.cancelation_reason}`);
// Check if this is a renegotiation
if (billing.cancelation_reason === 'customer_renegotiation') {
await handleRenegotiation(billing);
} else {
await handleRegularCancellation(billing);
}
}
// 3. Handle renegotiation specifically
async function handleRenegotiation(oldBilling) {
console.log(`Renegotiation detected for billing ${oldBilling.id}`);
// Fetch updated order to get new billing
const order = await fetchOrderFromAPI(oldBilling.order_id);
// Find the new billing
const newBilling = order.billings.find(b =>
b.origin_billing_id === oldBilling.id &&
b.status === 'waiting_payment'
);
if (!newBilling) {
console.error('Could not find new billing after renegotiation');
return;
}
console.log(`New billing created: ${newBilling.id}`);
console.log(`New terms: ${newBilling.installments}x of R$ ${newBilling.amount_cents / 100 / newBilling.installments}`);
// Update internal systems
await updateInternalBillingStatus(oldBilling.id, 'renegotiated');
await createInternalBillingRecord(newBilling);
// Notify customer
await notifyCustomerOfRenegotiation(order, oldBilling, newBilling);
}
// 4. Notify customer with new payment plan details
async function notifyCustomerOfRenegotiation(order, oldBilling, newBilling) {
const customerEmail = order.email.address;
const firstBill = newBilling.bills[0];
const emailContent = {
to: customerEmail,
subject: 'Your Payment Plan Has Been Updated',
body: `
Hello ${order.customer_name},
Your payment plan for order ${order.id} has been renegotiated.
Previous Plan:
- ${oldBilling.installments}x installments
- Total: R$ ${(oldBilling.amount_cents / 100).toFixed(2)}
New Plan:
- ${newBilling.installments}x installments
- Total: R$ ${(newBilling.amount_cents / 100).toFixed(2)}
- First payment due: ${formatDate(firstBill.due_date)}
- Amount per installment: R$ ${(firstBill.amount_cents / 100).toFixed(2)}
Your first payment under the new plan is due on ${formatDate(firstBill.due_date)}.
Thank you for your business!
`
};
await sendEmail(emailContent);
}
// 5. Helper function to fetch order from API
async function fetchOrderFromAPI(orderId) {
const response = await fetch(
`https://external-api-sandbox.lia.com.br/external_api/v1/orders/${orderId}`,
{
headers: {
'Authorization': `Bearer ${process.env.LIA_API_TOKEN}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to fetch order: ${response.statusText}`);
}
return await response.json();
}
Best Practices
1. Always Check cancelation_reason
Never assume all billing canceled events are the same. Always check the cancelation_reason field:
if (billing.status === 'canceled') {
switch (billing.cancelation_reason) {
case 'customer_renegotiation':
handleRenegotiation(billing);
break;
case 'down_payment_not_paid':
handleNonPayment(billing);
break;
case 'refunded_by_customer':
handleRefund(billing);
break;
default:
handleOtherCancellation(billing);
}
}
2. Fetch Fresh Order Data
After receiving a renegotiation webhook, always fetch the latest order data to get the new billing:
// Don't rely solely on webhook data
const order = await fetchOrder(billing.order_id);
const newBilling = order.billings.find(b => b.origin_billing_id === billing.id);
3. Implement Idempotent Webhook Handlers
Webhooks may be delivered multiple times. Ensure your handlers are idempotent:
async function handleRenegotiation(billing) {
// Check if we've already processed this renegotiation
const alreadyProcessed = await checkIfProcessed(billing.id);
if (alreadyProcessed) {
console.log(`Renegotiation ${billing.id} already processed, skipping`);
return;
}
// Process renegotiation
await processRenegotiation(billing);
// Mark as processed
await markAsProcessed(billing.id);
}
4. Handle Errors Gracefully
Always implement error handling and retry logic:
async function handleRenegotiation(billing) {
try {
await processRenegotiation(billing);
} catch (error) {
console.error(`Error processing renegotiation: ${error.message}`);
// Queue for retry
await queueForRetry(billing.id);
}
}
5. Log Renegotiation Events
Maintain detailed logs for troubleshooting:
await logEvent({
type: 'renegotiation',
old_billing_id: oldBilling.id,
new_billing_id: newBilling.id,
order_id: order.id,
old_terms: {
installments: oldBilling.installments,
amount: oldBilling.amount_cents
},
new_terms: {
installments: newBilling.installments,
amount: newBilling.amount_cents
},
timestamp: new Date().toISOString()
});
Summary
Key Points to Remember
- Renegotiation happens at the billing level, not the order level
- Order status remains
finishedthroughout the renegotiation process - Original billing is canceled with
cancelation_reason: customer_renegotiation - New billing is created immediately with status
waiting_payment - Only one webhook is triggered:
billing.canceledwith the specific cancelation reason - Always check the
cancelation_reasonfield to identify renegotiation events - Fetch fresh order data after receiving the webhook to get the new billing details
Related Documentation
- Order Schema - Complete order structure and status values
- Billing Schema - Complete billing structure and cancelation reasons
- Billing Events - All billing event types
- Webhooks - How to create and manage webhooks
- Use with Lia’s Checkout - Complete integration guide
Support
If you have any questions about renegotiation or need assistance with your integration, please contact our support team.