Contribution reporting

Understanding Contribution Reporting

What are Contribution Reports?

Kota’s contribution reporting system provides the data you need while allowing your payroll engine to handle the business logic. By integrating properly with this system, you can ensure accurate, compliant payroll processing across multiple countries and insurance providers with minimal manual intervention.

Contribution reports are a critical component for platforms that handle contributions - such as payroll and benefits platforms. They provide a structured way to access and process insurance contributions across multiple countries, enabling scalable systems and efficient reporting.

A contribution report represents a comprehensive breakdown of all insurance contributions for a specific employer over a defined period. Each report contains detailed employee breakdowns that include both employer and employee contributions.

For detailed API references, see the Contribution Reports API documentation.


Understanding Contribution Report Structure

Contribution reports consist of three main components that work together to provide a complete picture of insurance contributions:

  1. Contribution Report - The top-level container identified by a ctr_ prefix

    • Represents all contributions, employee made or employer made, for a specific employer during a specific period (usually a month)
    • Has a status of either open or finalized
  2. Employee Breakdowns - The per-employee details within a contribution report

    • Contains separate sections for employer and employee contributions to the policy
    • Employer and employee sections represent their respective contributions to the employee’s insurance - e.g. employer may pay for the main policy with the employee paying for dependents, employer may pay for the entire policy, and so forth
    • Employer contributions can be used for various payroll benefit calculations or reporting obligations, while employee contributions may be relevant to payroll deductions or reporting obligations.
    • Organized by insurance type (health, life, etc.)
  3. Individual Contributions - The actual monetary amounts identified by a ct_ prefix

    • Found within the employee breakdowns
    • Can be regular contributions or adjustments to previous contributions

When processing contribution reports, you’ll first retrieve the report, then access the employee breakdowns to get the individual contributions. The employer contributions and employee contributions are separated within each breakdown, allowing you to process them differently in your system.

For example, as mentioned above, employer contributions might be handled as company expenses and need to be reported to a national authority, while employee contributions could be deducted from employee paychecks. This separation gives you the flexibility to implement your own business logic and handle the complexity of international payroll and reporting requirements and changing regulatory environments.


Key Concepts

Report Status

Contribution reports have two possible states:

  • Open: The report can still be modified
  • Finalized: The report is immutable and serves as a historical record

Once a report is finalized, any subsequent changes or adjustments will be included in the next reporting period.

Contribution Adjustments

Adjustments are a mechanism to correct or modify previous contributions. When a contribution needs to be adjusted (due to changes in employee status, corrections, etc.), the adjustment is marked with:

  • An adjustment flag set to true
  • A reference to the original contribution being adjusted (adjustment_for)
  • Usually accompanied by explanatory notes

Reporting Periods

The various reports contains several different reporting and cover periods throughout, each relevant to what the report is being used for. Understanding these periods is crucial for accurate processing and reporting.

Contribution report:

Each contribution report covers a specific time period. This is to decide, for a given payroll run, which report you should use. For example, if you’re running June’s payroll, you might want to use either June’s report if finalizing it manually or use May’s (last month’s) completed report. These periods are defined as:

  • A start date (from_date)
  • An end date (to_date)

Both dates are formatted as YYYY-MM-DD.

Employee breakdown report:

The employee breakdown has periods split between the root of the report itself and within the employer and employee contributions.

The employee breakdown - period:

The period for which the employee breakdown is for. Note: This means it may span a specific calendar month while the contributions themselves may span that same month or a previous month. As a result you should be careful to ensure you only use this period when you explicitly want to reference the period this report spans. This time period is defined as:

  • A start date (from_date)
  • An end date (to_date)

Both dates are formatted as YYYY-MM-DD.

Individual employer / employee contributions - cover_period:

The period for which the insurance premium contribution covers the employee. It may differ from the report’s period. This allows for flexibility in reporting and processing contributions - see scenarios below to learn about the specific scenarios where this occurs. This cover period is defined as:

  • A start date (from_date)
  • An end date (to_date)

Both dates are formatted as YYYY-MM-DD.

Individual employer / employee contributions - reporting_month:

The primary month for which the contribution is relevant, helping to align contributions with standard monthly reporting cycles. It allows for consistent monthly categorisation in systems that require calendar-based reporting. Provided in YYYY-MM-DD format (with the day set to 01 as a convention), only the year and month are relevant.

Employee breakdown reports

This report contains the breakdown of contributions for each employee, split across the employer’s and employee’s own contributions. Focusing on the health_insurance object, common to both employer and employee, it’s useful to highlight some properties:

  • category: Category of the contribution gross_premium, tax, tax_relief - critical for how you process and report the contributions
  • member_type: Where available we split out contributions by policyholder, partner_dependent, child_dependent - this is useful for understanding the breakdown of contributions and how they are processed and may have reporting or tax implications in different jurisdictions
  • adjustment: Explained more elsewhere but this is where you’d locate and process an adjustment
  • adjustment_for: Unique identifier of the original contribution being adjusted - this is important for tracking and reconciling adjustments within your system and presenting it back to employers
  • note: Optional field for additional context, e.g. if the amount is pro-rated or backdated

Using Contribution Reports

Estimated Implementation Time

Estimated implementation time: 1-2 weeks, depending on the complexity of your system and how you handle adjustments and pro-rated amounts.

Factors affecting implementation time:

  • Complexity of your existing system
  • How you handle historical data and adjustments

Our implementation guide provides a clear path to integrate contribution reports into your system with clear code examples and scenarios through out for you which should help you decide on how much you need to support for your use-case and roadmapping.


Integrating contribution reports into your system involves a clear sequence of operations:

Step 1: Retrieve Reports

First you’ll need a finalized report to ensure you’re not working with changable data. Reports can be finalised in two ways:

  1. Finalize the report for the current month just before you need to use it, such as for running payroll or issuing reports for payroll to an employer. This ensures they’ll use the most fresh data.

  2. Wait for your report to be auto-finalized at the end of the month and then retrieve it on the first or later of the next month

  3. Finalize your report: Either finalize your report manually or wait for auto-finalization at month-end

  4. Retrieve the Report: Use the Retrieve a contribution report API to get the finalized report

  5. Check Report Status: Ensure the report status is finalized before processing as a safety check, especially in systems working with Events

Node.js
1const axios = require('axios');
2
3// Configure API client
4const kotaClient = axios.create({
5 baseURL: 'https://api.kota.io',
6 headers: {
7 'Authorization': `Bearer ${process.env.KOTA_API_KEY}`,
8 'Content-Type': 'application/json'
9 }
10});
11
12// Retrieve a contribution report
13async function getContributionReport(reportId) {
14 const response = await kotaClient.get(`/contribution_reports/${reportId}`);
15 return response.data;
16}

Step 2: Retrieve Employee Breakdowns

Once you have the contribution report, you need to retrieve the employee breakdowns:

Node.js
1// Retrieve all employee breakdowns for a report
2async function getEmployeeBreakdowns(reportId) {
3 const response = await kotaClient.get(`/contribution_reports/${reportId}/employee_breakdowns`);
4 return response.data.data;
5}
6
7// Retrieve a specific employee's breakdown
8async function getEmployeeBreakdown(reportId, employeeId) {
9 const response = await kotaClient.get(`/contribution_reports/${reportId}/employee_breakdowns/${employeeId}`);
10 return response.data;
11}

Step 3: Process Contributions

After retrieving the data, process each employee’s contributions:

  1. Distinguish Contribution Types:

    • Regular contributions: Process as normal additions to reports
    • Adjustments: Apply as corrections to previous periods
    • Pro-rated amounts: No special action needed as they appear like regular contributions but may contain a note
  2. Handle Each Contribution Type Appropriately:

    • Store contribution IDs for future reference (important for later adjustments)
    • Maintain a record of processed contributions
Node.js
1// Process employee breakdowns from a contribution report
2async function processEmployeeBreakdowns(reportId) {
3 // Get all employee breakdowns for this report
4 const breakdowns = await getEmployeeBreakdowns(reportId);
5
6 for (const breakdown of breakdowns) {
7 await processEmployeeContributions(breakdown);
8 }
9
10 console.log(`Successfully processed all employee breakdowns for report ${reportId}`);
11}
12
13// Process contributions for a single employee
14async function processEmployeeContributions(breakdown) {
15 const { employee_id, health_insurance } = breakdown;
16
17 // Process health insurance contributions
18 if (health_insurance) {
19 await processContributionSet(employee_id, health_insurance.employer_contributions, 'employer');
20 await processContributionSet(employee_id, health_insurance.employee_contributions, 'employee');
21 }
22
23 // handle other types of insurance in the future
24}

Common Scenarios

Scenario 1: Contribution Reports for Different Time Periods

Depending on insurer invoicing and report finalization timelines, contribution reports may contain contributions for different periods. Understanding these timing variations is crucial for correctly processing contributions in your system.

Previous Month Contributions

In this common scenario, May’s payroll would include contributions for April, which have been finalized after the insurer’s invoice was received:

  • Why this happens: Insurance providers often bill after the coverage period ends, once they’ve calculated actual costs
  • Timing considerations: There’s typically a delay between the end of a coverage period and when the report is finalized. This can occur when an insurer’s regular billing is in arrears rather than in advance (which is more common). This would cause the contributions to occur a month after the cover period. Another reason would be if your contribution report is finalized before the contributions for the current month are added (e.g. the insurer bills on the 15th but you finalize your report on the 10th)
  • Processing approach: These contributions could be treated as “catch-up” payments for services already provided, or as regular monthly contributions
  • Reconciliation needs: None, should be handled as further months come through as the insurer balances their collections

For example, if an April contribution report is finalized on May 5th, you would include these April contributions in your May payroll run. This ensures that employees and employers pay for the insurance coverage they received in April.

Current Month Contributions

In this scenario May’s payroll would include contributions from May’s cover period:

  • Why this happens: Some insurers bill in advance or in real-time for the current coverage period
  • Timing considerations: Likely to occur when an insurer bills in-advance, or more rarely if you manually finalise your reports during the month and the insurer finalises your contributions between the 1st and your finalization date
  • Processing approach: These are treated as current-period expenses, or as regular monthly contributions
  • Reconciliation needs: None, as the payment period aligns with the coverage period

This approach simplifies accounting as the payment period aligns with the coverage period, but requires reports to be finalized quickly at the beginning of each month.

Next Month Contributions

In advance billing scenarios, June contributions might be included in May’s payroll:

  • Why this happens: Some insurance providers require payment in advance of the coverage period
  • Timing considerations: Reports must be generated before the coverage period begins
  • Processing approach: These should be treated as prepaid expenses in your accounting system
  • Reconciliation needs: You may need to account for adjustments if employee status changes before the coverage period

This approach ensures that insurance providers receive payment before providing coverage, but requires your system to handle prepaid expenses and potential future adjustments.

1// Previous Month Contributions
2// The May payroll would include contributions for April
3const report = {
4 "id": "ctr_example123",
5 "employer_id": "er_abc123",
6 "period": {
7 "from_date": "2023-04-01",
8 "to_date": "2023-04-30"
9 },
10 "status": "finalized"
11};
12
13// Process April contributions in May payroll cycle
14async function processPreviousMonthContributions(report) {
15 console.log(`Processing contributions for ${report.period.from_date} to ${report.period.to_date}`);
16 // Implementation for previous month contributions
17}

Scenario 2: Contribution Reports with Adjustments

When corrections need to be made to previous contributions, they appear as adjustments in subsequent reports. Understanding how to process these adjustments is critical for maintaining accurate records and ensuring proper accounting.

What Are Contribution Adjustments?

Adjustments are special contribution entries that modify previously processed contributions. As mention above, they are characterized by:

  • An adjustment flag set to true to distinguish them from regular contributions
  • An adjustment_for field that references the ID of the original contribution being adjusted
  • An amount that can be positive (additional payment) or negative (refund/reduction)
  • Often include explanatory notes about the reason for the adjustment

Common Reasons for Adjustments

Adjustments typically occur due to:

  • Retroactive changes: Employee status or coverage level changed after a contribution was processed
  • Correction of errors: Fixing incorrect amounts from previous periods
  • Policy changes: Mid-period changes to insurance policies requiring recalculation
  • Reconciliation: Aligning actual costs with previously estimated amounts

Processing Adjustments in Your System

When handling adjustments, your system should:

  1. Identify the original contribution using the adjustment_for reference
  2. Apply the adjustment amount to correct the previous contribution
  3. Maintain an audit trail linking the adjustment to the original contribution
  4. Include explanatory notes in records for transparency

This approach ensures that your system accurately reflects all changes while maintaining a clear history of contributions and their adjustments.

1// Employee breakdown containing an adjustment
2const employeeBreakdown = {
3 "employee_id": "ee_xyz789",
4 "contribution_report_id": "ctr_example456",
5 "health_insurance": {
6 "employer_contributions": [
7 {
8 "id": "ct_adj123",
9 "adjustment": true,
10 "adjustment_for": "ct_orig456",
11 "amount": -50.00,
12 "note": "Correction for overpayment in previous period"
13 },
14 {
15 "id": "ct_reg789",
16 "adjustment": false,
17 "amount": 250.00
18 }
19 ]
20 }
21};
22
23// Process adjustments and regular contributions
24async function processAdjustmentsExample(breakdown) {
25 const { employee_id, health_insurance } = breakdown;
26
27 if (!health_insurance?.employer_contributions) return;
28
29 for (const contribution of health_insurance.employer_contributions) {
30 if (contribution.adjustment) {
31 // Handle adjustment
32 console.log(`Processing adjustment of ${contribution.amount} for contribution ${contribution.adjustment_for}`);
33 await applyAdjustment(employee_id, contribution);
34 } else {
35 // Handle regular contribution
36 console.log(`Processing regular contribution of ${contribution.amount}`);
37 await applyRegularContribution(employee_id, contribution);
38 }
39 }
40}

Scenario 3: Employees Joining a Group Policy

When employees join a policy (either at inception or mid-term), their initial contribution report may contain pro-rated amounts. This scenario requires special handling to ensure accurate processing and proper accounting for partial periods.

Understanding Pro-rated Contributions for New Employees

When an employee joins mid-month, their first contribution report typically includes:

  • Pro-rated contributions for the partial first month (from join date to month-end)
  • Full contributions for the upcoming complete month
  • Special notes indicating the join date and pro-rated nature of the contribution

How Pro-rata Works

Pro-rata is calculated based on the number of days an employee is covered in a partial month:

  • Calculation method: (Days covered / Total days in month) × Monthly premium
  • Coverage period: Clearly indicated in the cover_period field with specific from_date and to_date
  • Identification: Often includes notes mentioning “pro-rated” or “partial month”

Implementation Considerations

When processing pro-rated contributions for new employees, your system should:

  1. Identify pro-rated contributions by examining the cover period and notes
  2. Apply the correct amount to the appropriate period
  3. Maintain clear records of the employee’s join date and initial coverage
  4. Handle both the partial and full-month contributions in the same cycle if needed

This approach ensures that new employees are charged correctly from their first day of coverage, while maintaining accurate financial records for both the employer and employee.

Example: New Employee Scenario
1// Employee breakdown for a new joiner
2const newEmployeeBreakdown = {
3 "employee_id": "ee_new123",
4 "contribution_report_id": "ctr_example789",
5 "health_insurance": {
6 "employer_contributions": [
7 {
8 "id": "ct_prorate1",
9 "adjustment": false,
10 "amount": 83.33,
11 "note": "Pro-rated amount for partial previous month (joined on 2023-04-20)",
12 "cover_period": {
13 "from_date": "2023-04-20",
14 "to_date": "2023-04-30"
15 }
16 },
17 {
18 "id": "ct_full1",
19 "adjustment": false,
20 "amount": 250.00,
21 "cover_period": {
22 "from_date": "2023-05-01",
23 "to_date": "2023-05-31"
24 }
25 }
26 ]
27 }
28};
29
30// Process pro-rated contributions for a new employee
31async function processNewEmployeeContributions(breakdown) {
32 const { employee_id, health_insurance } = breakdown;
33
34 if (!health_insurance?.employer_contributions) return;
35
36 // Group contributions by period
37 const contributionsByPeriod = {};
38
39 for (const contribution of health_insurance.employer_contributions) {
40 if (!contribution.cover_period) continue;
41
42 const periodKey = `${contribution.cover_period.from_date}_${contribution.cover_period.to_date}`;
43
44 if (!contributionsByPeriod[periodKey]) {
45 contributionsByPeriod[periodKey] = [];
46 }
47
48 contributionsByPeriod[periodKey].push(contribution);
49 }
50
51 // Process each period's contributions
52 for (const [periodKey, contributions] of Object.entries(contributionsByPeriod)) {
53 const [fromDate, toDate] = periodKey.split('_');
54
55 // Determine if this is a pro-rated period
56 const isProRated = isPartialMonth(fromDate, toDate);
57
58 if (isProRated) {
59 console.log(`Processing pro-rated amount for period ${fromDate} to ${toDate}`);
60 // Your implementation for handling pro-rated amounts
61 } else {
62 console.log(`Processing full period amount for period ${fromDate} to ${toDate}`);
63 // Your implementation for handling full period amounts
64 }
65 }
66}
67
68// Helper function to determine if a period is a partial month
69function isPartialMonth(fromDate, toDate) {
70 const from = new Date(fromDate);
71 const to = new Date(toDate);
72
73 // Check if period starts on the 1st of the month
74 const startsOnFirstDay = from.getDate() === 1;
75
76 // Check if period ends on the last day of the month
77 const lastDayOfMonth = new Date(from.getFullYear(), from.getMonth() + 1, 0).getDate();
78 const endsOnLastDay = to.getDate() === lastDayOfMonth;
79
80 return !startsOnFirstDay || !endsOnLastDay;
81}

Scenario 4: Employees Leaving the Company

When employees leave but their final payroll is processed before the contribution report reflecting their departure, you’ll need to handle pro-rata reimbursements. This scenario requires careful handling to ensure proper accounting and to avoid overcharging for insurance coverage.

Understanding Employee Departure Refunds

When an employee leaves mid-month, the contribution report will typically include:

  • Negative adjustment to refund the unused portion of the month’s premium
  • Reference to the original contribution being adjusted via the adjustment_for field
  • Coverage period indicating the unused portion being refunded (from departure date to month-end)
  • Explanatory note mentioning the employee’s departure date

Timing Challenges with Employee Departures

A common challenge occurs when:

  1. An employee leaves mid-month (e.g., May 10th)
  2. Their final payroll is processed immediately (e.g., May 11th)
  3. The contribution report reflecting their departure isn’t finalized until later (e.g., June 1st)

This timing mismatch means you’ll need to handle the refund in a subsequent payroll cycle after the employee has already departed.

Implementation Considerations

When processing refunds for departed employees, your system should:

  1. Identify departure refunds by looking for negative adjustments with explanatory notes
  2. Link the refund to the original contribution using the adjustment_for reference
  3. Apply the refund to the employer’s account rather than the departed employee
  4. Maintain records of the departure date and refund for compliance and auditing

This approach ensures that employers receive appropriate refunds for departed employees while maintaining accurate financial records.

Example: Employee Leaving
1// Employee breakdown for a leaver
2const leaverBreakdown = {
3 "employee_id": "ee_leaving456",
4 "contribution_report_id": "ctr_nextperiod",
5 "health_insurance": {
6 "employer_contributions": [
7 {
8 "id": "ct_refund1",
9 "adjustment": true,
10 "adjustment_for": "ct_lastfull",
11 "amount": -166.67,
12 "note": "Pro-rata reimbursement for employee leaving on 2023-05-10",
13 "cover_period": {
14 "from_date": "2023-05-11",
15 "to_date": "2023-05-31"
16 }
17 }
18 ]
19 }
20};
21
22// Process refund for an employee who has left
23async function processEmployeeLeaverRefund(breakdown) {
24 const { employee_id, health_insurance } = breakdown;
25
26 if (!health_insurance?.employer_contributions) return;
27
28 // Find refund adjustments (negative amounts with adjustment flag)
29 const refundAdjustments = health_insurance.employer_contributions.filter(
30 contribution => contribution.adjustment && contribution.amount < 0
31 );
32
33 for (const refund of refundAdjustments) {
34 console.log(`Processing refund of ${Math.abs(refund.amount)} for employee ${employee_id} who left`);
35
36 // Retrieve the original contribution this is adjusting
37 const originalContributionId = refund.adjustment_for;
38 console.log(`This is an adjustment to contribution ${originalContributionId}`);
39
40 // Your implementation to process the refund in your system
41 await processRefund(employee_id, refund);
42 }
43}
44
45// Implementation for processing refunds
46async function processRefund(employeeId, refund) {
47 // Apply the refund to your system
48 // This might involve crediting the employer's account or adjusting future invoices
49 console.log(`Applied refund of ${Math.abs(refund.amount)} for employee ${employeeId}`);
50
51 // Record the refund for reconciliation
52 await recordRefund(employeeId, refund.id, refund.amount, refund.adjustment_for);
53}