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": "3c6d7169-75c1-567f-9b78-09657791d725",
"status": "finished", // Remains "finished" after renegotiation
"billings": [
// Will contain both old (canceled) and new (active) billings
]
}
For more details, see the Order schema documentation.
2. Original Billing is Canceled
The existing billing is canceled with the cancelation reason chosen for the renegotiation: customer_renegotiation (renegotiation without cancelation) or customer_dropout_with_fine (renegotiation with cancelation).
Webhook Event Triggered:
Entity: billing
Event: canceled
Cancelation Reason: customer_renegotiation or customer_dropout_with_fine
Canceled Billing Example:
{
"id": "f1e39993-6e5a-5f28-9ade-72f4132059fc",
"status": "canceled",
"cancelation_reason": "customer_renegotiation",
"order_id": "3c6d7169-75c1-567f-9b78-09657791d725",
"amount_cents": 299700,
"installments": 24,
"bills": [
{
"id": "5a1f0c3e-7b2d-5e4a-9c61-2f8d3b7e1a01",
"status": "paid",
"amount_cents": 12488
},
{
"id": "5a1f0c3e-7b2d-5e4a-9c61-2f8d3b7e1a02",
"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 active.
New Billing Properties:
- Status:
activeas soon as it is created - origin_billing_id: References the original billing ID
- New payment terms: Updated installment amounts, due dates, or payment schedule
New Billing Example:
{
"id": "ad25e510-8dfa-5f8f-b3fb-9534b1faa40a",
"status": "active",
"origin_billing_id": "f1e39993-6e5a-5f28-9ade-72f4132059fc",
"order_id": "3c6d7169-75c1-567f-9b78-09657791d725",
"amount_cents": 287212,
"installments": 18,
"due_day": 15,
"bills": [
{
"id": "5a1f0c3e-7b2d-5e4a-9c61-2f8d3b7e1b01",
"status": "pending",
"amount_cents": 15956,
"due_date": "2024-04-15T23:59:59.999-03:00"
},
{
"id": "5a1f0c3e-7b2d-5e4a-9c61-2f8d3b7e1b02",
"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 its renegotiation cancelation reason)
- 1 active billing (the new billing with updated terms)
Complete Order Example After Renegotiation:
{
"id": "3c6d7169-75c1-567f-9b78-09657791d725",
"status": "finished",
"customer_name": "João Silva",
"customer_document": "12345678901",
"customer_type": "person",
"billings": [
{
"id": "f1e39993-6e5a-5f28-9ade-72f4132059fc",
"status": "canceled",
"cancelation_reason": "customer_renegotiation",
"amount_cents": 299700,
"installments": 24,
"origin_billing_id": null
},
{
"id": "ad25e510-8dfa-5f8f-b3fb-9534b1faa40a",
"status": "active",
"cancelation_reason": null,
"amount_cents": 287212,
"installments": 18,
"origin_billing_id": "f1e39993-6e5a-5f28-9ade-72f4132059fc"
}
]
}
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 two billing webhooks: canceled for the original billing, with its cancelation reason, and active for the new billing, with origin_billing_id set to the original billing’s id.
Setting Up the Webhook
To listen for renegotiation events, create a webhook for billing canceled and active 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", "active"],
"verb": "post"
}'
For more information about webhooks, see the Webhooks documentation.
Webhook Payload Example
When a renegotiation happens, you will receive this payload for the original billing (data abbreviated; see Billing Events for the full payload):
{
"api_version": "1.0",
"entity": "billing",
"event": "canceled",
"data": {
"id": "f1e39993-6e5a-5f28-9ade-72f4132059fc",
"order_id": "3c6d7169-75c1-567f-9b78-09657791d725",
"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": "7c2e9d41-3a5b-5f68-8e1d-4b6a2c9f0d13",
"payment_type": "financed",
"financed_type": "bolepix"
},
"bills": [
{
"id": "5a1f0c3e-7b2d-5e4a-9c61-2f8d3b7e1a01",
"status": "paid",
"amount_cents": 12488
},
{
"id": "5a1f0c3e-7b2d-5e4a-9c61-2f8d3b7e1a02",
"status": "canceled",
"amount_cents": 12488
}
],
"contact": {
"email": "joao.silva@example.com",
"phone": "5511999999999"
},
"offer": {
"id": "8dd4fffd-e1b3-503f-a7f2-963c6d32c0cd",
"name": "Padrão VIA API",
"amount_cents": 299700,
"product": {
"id": "a622575f-2f5d-532f-9c8f-22e33593d3ce",
"name": "Curso Exemplo",
"description": ""
}
},
"order_bump_offers": []
},
"id": "3b5d5c3712955042212316173ccd8b56"
}
Identifying Renegotiation Events
To specifically identify a renegotiation, check origin_billing_id on the active billing webhook: it is set only on the new billing created by a renegotiation. The cancelation reason of the original billing (customer_renegotiation or customer_dropout_with_fine) is not enough on its own.
// Example webhook handler
function handleBillingActive(webhookData) {
const billing = webhookData.data;
if (billing.origin_billing_id) {
// This billing was created by a renegotiation
handleRenegotiation(billing);
}
}
function handleRenegotiation(newBilling) {
// 1. The original billing is the one referenced by origin_billing_id
const order = fetchOrder(newBilling.order_id);
const oldBilling = order.billings.find(b => b.id === newBilling.origin_billing_id);
// 2. Update your internal systems with the new billing information
updateInternalBilling(oldBilling, newBilling);
// 3. Notify customer about the renegotiation
notifyCustomer(newBilling.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 | The lower of the original configuration’s minimum and 2,000 cents (R$ 20.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 { entity, event, data: billing } = req.body;
// Acknowledge receipt immediately
res.status(200).json({ received: true });
// Process webhook asynchronously
if (entity === 'billing' && event === 'canceled') {
await handleBillingCanceled(billing);
}
if (entity === 'billing' && event === 'active' && billing.origin_billing_id) {
await handleRenegotiation(billing);
}
});
// 2. Handle billing canceled events
async function handleBillingCanceled(billing) {
console.log(`Billing ${billing.id} was canceled`);
console.log(`Reason: ${billing.cancelation_reason}`);
// A renegotiation is handled when the new billing's `active` webhook arrives
await handleRegularCancellation(billing);
}
// 3. Handle renegotiation specifically (new billing with origin_billing_id)
async function handleRenegotiation(newBilling) {
console.log(`Renegotiation detected: billing ${newBilling.id} replaces ${newBilling.origin_billing_id}`);
// Fetch updated order to get the original billing
const order = await fetchOrderFromAPI(newBilling.order_id);
const oldBilling = order.billings.find(b => b.id === newBilling.origin_billing_id);
if (!oldBilling) {
console.error('Could not find the original billing of the 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':
case 'customer_dropout_with_fine':
// May be a renegotiation: confirm with the new billing's origin_billing_id
handlePossibleRenegotiation(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_renegotiationorcustomer_dropout_with_fine - New billing is created immediately with status
active - Two billing webhooks are triggered:
canceledfor the original billing andactivefor the new one - Check
origin_billing_idon the new billing 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.