Connect
v2025-08-28Support

Receivables API

The Receivables API enables platforms to submit unpaid invoices for recovery. Support both structured data submission and PDF processing with OCR.

Warning

Prerequisites: Users must complete onboarding before submitting receivables. Check user.capabilities.send_receivables before allowing submissions.

Submission Methods

Method 1: Structured Data

Submit receivables with complete structured data:

javascript
const receivable = await cleavr.receivables.create({
// User identification
user_id: 'usr_clv_a1b2c3d4e5f6',
// Invoice details
invoice: {
numbers: ['INV-2025-001', 'INV-2025-002'], // Support multiple invoices
issue_date: '2025-08-01',
due_date: '2025-08-31',
service_description: 'Consulting services for Q3 2025',
// Amounts
amount: 5000.00, // Total including tax
amount_before_tax: 4166.67,
tax_amount: 833.33,
currency: 'EUR',
// Optional
payment_terms: 'Net 30',
late_penalty_rate: 10.0, // Annual percentage
},
// Debtor information
debtor: {
// Company details
company_name: 'Debtor Corp',
company_type: 'company', // 'company' or 'individual'
siret: '98765432100001',
vat_number: 'FR98765432101',
// Address
address: {
line1: '456 Avenue des Champs',
line2: 'Building B',
city: 'Lyon',
postal_code: '69000',
country: 'FR'
},
// Contact information (CRITICAL for success)
contacts: [
{
first_name: 'Marie',
last_name: 'Martin',
email: 'marie.martin@debtorcorp.com', // REQUIRED
phone: '+33612345678', // HIGHLY RECOMMENDED
job_title: 'CFO',
level: 0 // Primary contact
},
{
first_name: 'Pierre',
last_name: 'Dubois',
email: 'pierre.dubois@debtorcorp.com',
phone: '+33698765432',
job_title: 'Accounting Manager',
level: 1 // Secondary contact for escalation
}
]
},
// Platform metadata
metadata: {
platform_invoice_id: 'plat_inv_123',
customer_id: 'cust_456',
notes: 'Customer disputed item 3, but agreed to pay rest'
}
});

Method 2: PDF with OCR

Submit PDF invoices for automatic extraction:

javascript
// First, upload the PDF
const formData = new FormData();
formData.append('file', pdfFile);
formData.append('user_id', 'usr_clv_a1b2c3d4e5f6');
const extraction = await cleavr.receivables.extractFromPDF(formData);
// Review and complete extraction
if (extraction.confidence < 0.8 || extraction.missing_fields.length > 0) {
// Complete missing information
const receivable = await cleavr.receivables.create({
...extraction.data,
// Add missing fields
debtor: {
...extraction.data.debtor,
contacts: [{
email: 'contact@debtor.com', // Add if missing
phone: '+33612345678'
}]
}
});
}

OCR Extraction Response:

json
{
"extraction_id": "ext_a1b2c3d4",
"confidence": 0.85,
"data": {
"invoice": {
"numbers": ["INV-2025-001"],
"amount": 5000.00,
"due_date": "2025-08-31"
// ... extracted fields
},
"debtor": {
"company_name": "Debtor Corp"
// ... extracted fields
}
},
"missing_fields": [
"debtor.contacts[0].email",
"debtor.contacts[0].phone"
],
"warnings": [
{
"field": "debtor.siret",
"message": "SIRET format appears invalid",
"extracted_value": "987654321"
}
]
}

Contact Requirements & Success Rates

Info

Email is MANDATORY: Receivables without debtor email will be rejected. Phone is CRUCIAL: Success rate is highly increased.

Contact Priority Levels

Contacts are processed in priority order during escalation:

javascript
contacts: [
{ level: 0, ... }, // Primary - contacted first
{ level: 1, ... }, // Secondary - if primary doesn't respond
{ level: 2, ... }, // Tertiary - final escalation
]

Handling Missing Contacts

javascript
try {
const receivable = await cleavr.receivables.create(data);
} catch (error) {
if (error.code === 'MISSING_EMAIL_CONTACT') {
// Must add email before proceeding
const updatedData = {
...data,
debtor: {
...data.debtor,
contacts: [{
email: await promptUserForEmail(),
phone: await promptUserForPhone(),
first_name: 'Contact',
last_name: 'Person'
}]
}
};
const receivable = await cleavr.receivables.create(updatedData);
} else if (error.code === 'MISSING_PHONE_WARNING') {
// Warning about lower success rate
const proceed = await confirmWithUser(
'Missing phone number reduces success rate from 82% to 45%. Continue?'
);
if (proceed) {
const receivable = await cleavr.receivables.create({
...data,
force_proceed: true
});
}
}
}

Receivable Lifecycle

mermaid
stateDiagram-v2
[*] --> pending: Create Receivable
pending --> in_process: Send to Recovery
in_process --> payment_received: Debtor Pays
in_process --> disputed: Debtor Disputes
in_process --> cancelled: Platform Cancels
payment_received --> recovered: Payment Confirmed
disputed --> evidence_requested: Request Evidence
evidence_requested --> in_process: Evidence Provided
evidence_requested --> cancelled: No Evidence

Status Definitions

StatusDescriptionNext Actions
pendingCreated but not sent to recoverySend to recovery or cancel
in_processActive recovery in progressMonitor, update, or cancel
payment_receivedPayment received, pending confirmationWait for confirmation
recoveredSuccessfully recoveredNone (terminal state)
disputedDebtor disputes the debtProvide evidence
evidence_requestedWaiting for supporting documentsSubmit evidence
cancelledRecovery cancelledNone (terminal state)

Managing Receivables

Get Receivable Status

javascript
const receivable = await cleavr.receivables.get('rec_a1b2c3d4e5f6');
console.log(receivable);
// {
// id: 'rec_a1b2c3d4e5f6',
// status: 'in_process',
// amount: 5000.00,
// recovery_progress: {
// emails_sent: 3,
// calls_made: 1,
// last_contact: '2025-09-15T10:30:00Z',
// next_action: 'phone_call',
// next_action_date: '2025-09-17T09:00:00Z'
// },
// commission: {
// rate: 15.90, // Based on 60-day old debt
// amount: 795.00
// },
// estimated_recovery_date: '2025-09-30'
// }

Update Receivable

Add or update debtor information:

javascript
// Add new contact
await cleavr.receivables.addContact('rec_a1b2c3d4e5f6', {
first_name: 'New',
last_name: 'Contact',
email: 'new.contact@debtor.com',
phone: '+33611111111',
level: 2 // Tertiary contact
});
// Update metadata
await cleavr.receivables.update('rec_a1b2c3d4e5f6', {
metadata: {
...existingMetadata,
internal_note: 'Customer promised payment by end of month'
}
});

Cancel Recovery

Stop recovery process:

javascript
await cleavr.receivables.cancel('rec_a1b2c3d4e5f6', {
reason: 'paid_directly', // Required
notes: 'Customer paid directly to our bank account'
});
// Cancellation reasons:
// - 'paid_directly': Debtor paid outside of Cleavr
// - 'disputed_valid': Valid dispute from debtor
// - 'business_decision': Strategic decision to stop
// - 'incorrect_submission': Error in receivable data

Handling Disputes

When Disputes Occur

Disputes automatically pause recovery:

javascript
// Webhook notification
{
"event": "receivable.disputed",
"data": {
"receivable_id": "rec_a1b2c3d4e5f6",
"dispute": {
"reason": "service_not_delivered",
"debtor_message": "We never received the products mentioned in invoice",
"evidence_requested": [
"delivery_confirmation",
"signed_contract"
]
}
}
}

Submit Evidence

Provide supporting documents:

javascript
const evidence = await cleavr.receivables.submitEvidence('rec_a1b2c3d4e5f6', {
documents: [
{
type: 'delivery_confirmation',
file: deliveryProofFile, // File or base64
description: 'Signed delivery receipt from 2025-08-15'
},
{
type: 'contract',
file: contractFile,
description: 'Signed service agreement'
}
],
statement: 'Products were delivered on Jan 15, 2024, as confirmed by the attached signed receipt.'
});

Partial Payments

Handle partial payment scenarios:

javascript
// Webhook notification for partial payment
{
"event": "receivable.partial_payment",
"data": {
"receivable_id": "rec_a1b2c3d4e5f6",
"original_amount": 5000.00,
"paid_amount": 3000.00,
"remaining_amount": 2000.00,
"action_required": "platform_decision"
}
}
// Platform decides how to proceed
await cleavr.receivables.handlePartialPayment('rec_a1b2c3d4e5f6', {
action: 'continue_recovery', // or 'mark_complete'
notes: 'Continue recovering remaining €2000'
});

Commission Calculation

Get Commission Estimate

Calculate commission before submission:

javascript
const commission = await cleavr.commission.calculate({
amount: 5000.00,
invoice_date: '2025-08-01', // Used to calculate age
platform_id: 'plat_live_xxx' // For custom rates
});
// Response
{
"rate_percentage": 15.90,
"rate_tier": "45-89 days",
"commission_amount": 795.00,
"net_recovery": 4205.00,
"custom_rate": false // true if platform has special rate
}

Commission Rate Table

javascript
const rates = await cleavr.commission.getRates();
// Response
{
"default_rates": [
{ "days_from": 0, "days_to": 44, "rate": 10.80 },
{ "days_from": 45, "days_to": 89, "rate": 15.90 },
// ...
],
"platform_custom_rate": {
"enabled": true,
"flat_rate": 12.50, // If negotiated
"volume_tiers": [...] // If volume-based
}
}

Bulk Operations

Submit Multiple Receivables

javascript
const results = await cleavr.receivables.createBatch([
{ /* receivable 1 */ },
{ /* receivable 2 */ },
// ... up to 100 per batch
]);
// Response
{
"successful": [
{ "index": 0, "id": "rec_xxx", "status": "created" },
{ "index": 2, "id": "rec_yyy", "status": "created" }
],
"failed": [
{
"index": 1,
"error": "MISSING_EMAIL_CONTACT",
"message": "Debtor email is required"
}
]
}

List Receivables

javascript
const receivables = await cleavr.receivables.list({
user_id: 'usr_clv_xxx',
status: 'in_process',
created_after: '2025-01-01',
created_before: '2025-12-31',
limit: 50,
starting_after: 'rec_xxx' // For pagination
});

Idempotency

Prevent duplicate submissions:

javascript
const receivable = await cleavr.receivables.create(data, {
idempotency_key: 'platform-inv-12345'
});
// Subsequent calls with same key return existing receivable
const same = await cleavr.receivables.create(data, {
idempotency_key: 'platform-inv-12345'
});
console.log(receivable.id === same.id); // true

Error Handling

Common Errors

Error CodeDescriptionSolution
USER_NOT_ONBOARDEDUser hasn't completed KYBRedirect to onboarding
MISSING_EMAIL_CONTACTNo email providedAdd email before submission
INVALID_SIRETInvalid French company IDVerify and correct SIRET
DUPLICATE_RECEIVABLEAlready submittedCheck with idempotency key
INVALID_CURRENCYUnsupported currencyConvert to EUR

Error Response Format

json
{
"error": {
"code": "MISSING_EMAIL_CONTACT",
"message": "At least one debtor contact with email is required",
"field": "debtor.contacts[0].email",
"details": {
"debtor_name": "Debtor Corp",
"has_phone": true,
"has_email": false
}
}
}

Next Steps

After submitting receivables:

  1. Configure Webhooks for real-time updates
  2. Monitor Recovery Progress
  3. Handle Disputes