> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.kota.io/api-reference/contribution-reports/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.kota.io/_mcp/server. ## 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](/api-reference/contribution-reports/retrieve-contribution-report). --- ## 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, whether 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, or employer may pay for the entire policy) * 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 Lifecycle Contribution reports have two possible states: * **Open**: The report can still be modified as new data is processed * **Finalized**: The report is immutable and serves as a historical record Reports can be finalized at any point during the month by calling the [Finalize contribution report](/api-reference/contribution-reports/finalize-contribution-report) API. If not explicitly finalized, they are automatically finalized at the end of the month. **Important**: Once a report is finalized, any new contributions that arrive afterward during the same month will appear in the next month's report. This is the standard behavior for late-arriving data, not an adjustment scenario. Reports become available from the 1st of each month or after the finalization of the previous month's report. ### 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 contain 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. | Field | Location | Purpose | | ----------------- | ------------------------ | ------------------------------------------- | | `period` | Report & breakdown root | The reporting month this report belongs to | | `cover_period` | Individual contributions | The month the insurance coverage applies to | | `reporting_month` | Individual contributions | Primary month for reporting alignment | #### Contribution report - period Each contribution report has a `period` property that covers a specific time period. This helps you decide which report to use for a given payroll run. For example, if you're running June's payroll, you might want to use either June's report (if finalizing it manually) or May's completed report. The `period` property is defined as a start date (`from_date`) and an end date (`to_date`). Both dates are formatted as `YYYY-MM-DD`. #### Employee breakdown report - periods Employee breakdowns contain different period properties at different levels of the data structure. ##### The employee breakdown - `period`: The `period` property at the root of the employee breakdown is identical to the `period` of the parent contribution report. This represents the reporting period that the breakdown belongs to. Note: While the breakdown's `period` represents the reporting month, the individual contributions within it may have different `cover_period` values spanning current, previous, or future months. ##### 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. The `cover_period` property is defined as a start date (`from_date`) and an end date (`to_date`). Both dates are formatted as `YYYY-MM-DD`. Cover periods can vary in two important ways: * **Different month boundaries**: While most coverage periods run from the 1st to the end of the month, some may start on any date (e.g. from the 10th of one month to the 9th of the next month) * **Partial periods**: Coverage can be shorter than one month, particularly for mid-term joiners or leavers who have pro-rated coverage ##### Individual employer / employee contributions - `reporting_month`: The primary month for which the contribution is relevant as it helps align contributions with standard monthly reporting cycles. It allows for consistent monthly categorisation in systems that require calendar-based reporting. The `reporting_month` property is: * Always present for every contribution entry * Guaranteed to be in `YYYY-MM-01` format (the day is always `01` by convention) * Typically based on the start date of the period for cross-month cover periods (e.g., 10th to 9th) * Consistent and stable for operational categorization ### Key Properties in Employee Breakdowns 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_dependant`, `child_dependant` - 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`: References the `id` field 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 Employee breakdown responses also include a `currency` field indicating the currency for all monetary amounts in the breakdown (e.g., `eur`, `usd`). --- ## Using Contribution Reports ### Estimated Implementation Time **Estimated implementation time**: 1-2 weeks, depending on: * 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: Discover Available Reports Before you can process contribution reports, you need to identify which reports are available for your use. Use the [List contribution reports](/api-reference/contribution-reports/list-contribution-reports) API to discover available reports with filtering options. **`Node.js`** ```javascript Node.js // List all contribution reports for a specific employer async function listContributionReports(employerId, options = {}) { const params = { employer_id: employerId, ...options }; const response = await kotaClient.get('/contribution_reports', { params }); return response.data.data; } // Example: Get reports for payroll processing (any status) async function getReportsForPayroll(employerId, year, month) { const reports = await listContributionReports(employerId, { year: year, month: month }); return reports; } ``` **`Response`** ```json Response { "items": [ { "object": "contribution_report", "id": "ctr_7d37c383d2bf49c4beea3ebdbe4de787", "employer_id": "er_01227f212421474cbe687cde68568d37", "external_customer_id": null, "period": { "from_date": "2025-03-01", "to_date": "2025-03-31" }, "status": "open", "finalized_at": null, "created_at": "2025-03-10T20:53:14.196315Z", "last_updated_at": "2025-04-01T00:00:10.323195Z" } ], "page": 1, "page_size": 10, "total_count": 1, "has_next_page": false, "has_previous_page": false } ``` Common filtering scenarios: * **For payroll processing**: Filter by `employer_id` and specific `year`/`month` * **For EoR platforms**: Add `external_customer_id` to separate reports by customer * **For historical analysis**: Use `status: 'finalized'` with date range filtering for completed periods ### Step 2: Finalize the Report (if needed) If the report is in `open` status, you need to finalize it before processing to ensure the data is immutable. If it's already `finalized`, you can skip this step. Use the [Finalize contribution report](/api-reference/contribution-reports/finalize-contribution-report) API. For payroll processing, the recommended approach is to finalize the report immediately before using it for locking your payroll run. For example, if you're running payroll on September 22nd, you should finalize September's report on that same day before processing payroll. **`Node.js`** ```javascript Node.js // Complete workflow: discover and conditionally finalize async function prepareReportForPayroll(employerId, year, month) { // Step 1: Find reports for the specified month const reports = await getReportsForPayroll(employerId, year, month); if (reports.length === 0) { throw new Error(`No reports found for ${year}-${month}`); } const report = reports[0]; console.log(`Found report: ${report.id} (status: ${report.status})`); // Step 2: Finalize if needed if (report.status !== 'finalized') { console.log(`Report ${report.id} is not finalized, finalizing...`); await kotaClient.post(`/contribution_reports/${report.id}/finalize`); console.log(`Report ${report.id} finalized successfully`); } else { console.log(`Report ${report.id} is already finalized`); } return report.id; // Return the ID for the next step } ``` **`Response`** ```json Response { "object": "contribution_report", "id": "ctr_7d37c383d2bf49c4beea3ebdbe4de787", "employer_id": "er_01227f212421474cbe687cde68568d37", "external_customer_id": null, "period": { "from_date": "2025-03-01", "to_date": "2025-03-31" }, "status": "finalized", "finalized_at": "2025-04-01T00:00:10.311966Z", "created_at": "2025-03-10T20:53:14.196315Z", "last_updated_at": "2025-04-01T00:00:10.323195Z" } ``` ### Step 3: Retrieve Employee Breakdowns Once you have the finalized contribution report, you need to retrieve the employee breakdowns: * **List All Breakdowns**: Use [List employee breakdowns](/api-reference/contribution-reports/list-employee-breakdowns-for-contribution-report) to get all employee data at once * **Or Get Individual Breakdowns**: For targeted processing, use [Retrieve individual employee breakdown](/api-reference/contribution-reports/retrieve-employee-breakdown-for-contribution-report) with employee IDs **`Node.js`** ```javascript Node.js // Retrieve all employee breakdowns for a report async function getEmployeeBreakdowns(reportId) { const response = await kotaClient.get(`/contribution_reports/${reportId}/employee_breakdowns`); return response.data.data; } // Retrieve a specific employee's breakdown async function getEmployeeBreakdown(reportId, employeeId) { const response = await kotaClient.get(`/contribution_reports/${reportId}/employee_breakdowns/${employeeId}`); return response.data; } ``` **`Response`** ```json Response { "object": "contribution_report_employee_breakdown", "employee_id": "ee_c245fde199004eee845b2c26a5732ce2", "contribution_report_id": "ctr_7d37c383d2bf49c4beea3ebdbe4de787", "employer_id": "er_01227f212421474cbe687cde68568d37", "external_customer_id": null, "period": { "from_date": "2025-03-01", "to_date": "2025-03-31" }, "currency": "eur", "status": "finalized", "finalized_at": "2025-04-01T00:00:10.311966Z", "created_at": "2025-03-10T20:53:14.196315Z", "last_updated_at": "2025-04-01T00:00:10.323195Z", "health_insurance": { "employer_contributions": [ { "id": "ct_0447692a1bf74400965b1c1b00f33610", "category": "tax", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 0.2, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-04-01", "to_date": "2025-04-30" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-04-01" }, { "id": "ct_8db2eb4b9d9446a0b9fef29a7f3c36c9", "category": "gross_premium", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 133.54, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-03-01", "to_date": "2025-03-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-03-01" } ], "employee_contributions": [ { "id": "ct_a468afd6d35242a4af2239d014804018", "category": "tax", "member_type": "partner_dependant", "adjustment": false, "adjustment_for": null, "amount": 0.2, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-04-01", "to_date": "2025-04-30" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-04-01" }, { "id": "ct_c0d990c990d34ae09ab8970a0bb921ef", "category": "gross_premium", "member_type": "partner_dependant", "adjustment": false, "adjustment_for": null, "amount": 133.54, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-03-01", "to_date": "2025-03-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-03-01" } ] } } ``` ### Step 4: 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`](/api-reference/contribution-reports/retrieve-employee-breakdown-for-contribution-report#response.body.health_insurance.employer_contributions.note) field 2. **Handle Each Contribution Type Appropriately**: * Store contribution IDs for future reference (important for later adjustments) * Maintain a record of processed contributions **`Node.js`** ```javascript Node.js // Process employee breakdowns from a contribution report async function processEmployeeBreakdowns(reportId) { // Get all employee breakdowns for this report const breakdowns = await getEmployeeBreakdowns(reportId); for (const breakdown of breakdowns) { await processEmployeeContributions(breakdown); } console.log(`Successfully processed all employee breakdowns for report ${reportId}`); } // Process contributions for a single employee async function processEmployeeContributions(breakdown) { const { employee_id, health_insurance } = breakdown; // Process health insurance contributions if (health_insurance) { await processContributionSet(employee_id, health_insurance.employer_contributions, 'employer'); await processContributionSet(employee_id, health_insurance.employee_contributions, 'employee'); } // handle other types of insurance in the future } ``` ## 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 scenario, May's payroll would include contributions for April coverage, which appear in the May contribution report. When you process your May payroll run, you would include these April contributions to ensure employees and employers pay for the insurance coverage that was provided in April. * **Why this happens**: There are two main cases where April contributions appear in your May report: 1. **Insurer timing**: The insurer provides contribution data in arrears. For example, data for April coverage is provided on May 1st. When Kota receives this data, the April contributions are added to your May report. 2. **Report finalization timing**: The insurer provides contribution data during the same month as the coverage, but after you've already finalized your report for that month. For example, you finalize your April report on April 10th, but the insurer provides data on April 12th. These contributions roll over to your May report. * **Processing approach**: Treat these as regular monthly contributions that happen to cover a previous period. * **Reconciliation needs**: None required, as this is normal timing variation in insurance processing. #### Current Month Contributions In this scenario, May's payroll would include contributions for May coverage, which appear in the May contribution report. When you process your May payroll run, you would include these May contributions to ensure employees and employers pay for the insurance coverage that will be provided in May. * **Why this happens**: There are two main cases where May contributions appear in your May report: 1. **Insurer timing**: The insurer provides contribution data in advance for the current month. For example, on April 1st, they provide data for April coverage. When Kota receives this data, the April contributions are added to your April report. 2. **Report finalization timing**: The insurer provides contribution data for a future month, but after you've already finalized your current report. For example, you finalize your April report on April 10th, but on April 28th you receive data for May in advance. These May contributions roll over to your May report. * **Processing approach**: Treat these as regular monthly contributions where the coverage period aligns with the payment period. * **Reconciliation needs**: Adjustments may occur in the next month if employee status changes happen during the coverage month after finalization or cut-off dates. #### Next Month Contributions In this scenario, May's payroll would include contributions for June coverage, which appear in the May contribution report. When you process your May payroll run, you would include these June contributions to ensure employees and employers pay for the insurance coverage that will be provided in June. * **Why this happens**: The insurer requires advance payment for future coverage. For example, on May 15th, they provide data for June coverage requiring payment before the coverage period begins. When Kota receives this data, the June contributions are added to your May report. * **Processing approach**: Treat these as prepaid expenses in your accounting system, as payment occurs before the coverage period. * **Reconciliation needs**: You may need to account for adjustments if employee status changes before the coverage period ends. #### Cross-Month Coverage Periods In this scenario, May's payroll might include contributions with coverage periods that span across calendar months, such as mid-April to mid-May or mid-May to mid-June. * **Why this happens**: Some insurers allow coverage periods to start on any date during the month, not just the 1st. This creates coverage periods that don't align with standard calendar months. * **Processing approach**: * Use the `cover_period` dates to understand the actual coverage timeframe * For systems requiring strict calendar month assignment, use the `reporting_month` property which provides a standardized monthly categorization * Treat these as regular contributions while being mindful of the non-standard coverage periods * **Reconciliation needs**: Standard adjustment handling applies, with particular attention to changes that occur during the cross-month coverage period. For example, if contribution data with a coverage period from April 10th to May 9th is received by Kota on May 5th, it will be included in your May contribution report. The `reporting_month` field will help you categorize this contribution consistently with your monthly reporting cycles. **`Node.js`** ```javascript Node.js // Process contributions with various timing scenarios using reporting_month for categorization // Note: Adjustment handling is omitted for brevity - see Scenario 2 for adjustment processing async function processEmployeeContributions(breakdown) { const { employee_id, health_insurance } = breakdown; if (!health_insurance?.employer_contributions) return; for (const contribution of health_insurance.employer_contributions) { // Use reporting_month for consistent monthly categorization const reportingMonth = contribution.reporting_month.substring(0, 7); // Extract YYYY-MM // Process the contribution amount for payroll await processPayrollContribution(employee_id, contribution.amount, reportingMonth); // Store coverage details for auditing and transparency await auditContributionCoverage({ contributionId: contribution.id, employeeId: employee_id, amount: contribution.amount, coveragePeriod: contribution.cover_period, reportingMonth: reportingMonth, category: contribution.category, memberType: contribution.member_type, isAdjustment: contribution.adjustment, adjustmentFor: contribution.adjustment_for, note: contribution.note }); } } // Example: Categorize contributions by reporting month for monthly reports function categorizeContributionsByMonth(contributions) { const monthlyBreakdown = {}; contributions.forEach(contribution => { const month = contribution.reporting_month.substring(0, 7); if (!monthlyBreakdown[month]) { monthlyBreakdown[month] = { totalAmount: 0, contributions: [] }; } monthlyBreakdown[month].totalAmount += contribution.amount; monthlyBreakdown[month].contributions.push({ id: contribution.id, amount: contribution.amount, coveragePeriod: contribution.cover_period, note: contribution.note }); }); return monthlyBreakdown; } ``` ### 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. **`Node.js`** ```javascript Node.js // Process contributions and handle adjustments async function processContributions(breakdown) { const { employee_id, health_insurance } = breakdown; if (!health_insurance?.employer_contributions) return; for (const contribution of health_insurance.employer_contributions) { if (contribution.adjustment) { // Handle adjustment - link to previous contribution in your system console.log(`Processing adjustment of ${contribution.amount} for original contribution ${contribution.adjustment_for}`); // Look up the original contribution in your database using the adjustment_for ID const originalContribution = await lookupContribution(contribution.adjustment_for); // Apply the adjustment and maintain the link for reconciliation await applyAdjustment(employee_id, contribution, originalContribution); } else { // Handle regular contribution console.log(`Processing regular contribution of ${contribution.amount}`); await applyRegularContribution(employee_id, contribution); } } } ``` **`May Report`** ```json May Report { "object": "contribution_report_employee_breakdown", "employee_id": "ee_abc123def456789012345678901234567890", "contribution_report_id": "ctr_may2025abc123def456789012345678901234", "period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "health_insurance": { "employer_contributions": [ { "id": "ct_111111111111111111111111111111111111", "category": "gross_premium", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 300.00, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "reporting_month": "2025-05-01" } ] } } ``` **`June Report`** ```json June Report { "object": "contribution_report_employee_breakdown", "employee_id": "ee_abc123def456789012345678901234567890", "contribution_report_id": "ctr_june2025def456789012345678901234567", "period": { "from_date": "2025-06-01", "to_date": "2025-06-30" }, "health_insurance": { "employer_contributions": [ { "id": "ct_222222222222222222222222222222222222", "category": "gross_premium", "member_type": "policyholder", "adjustment": true, "adjustment_for": "ct_111111111111111111111111111111111111", "amount": -50.00, "type": "recurring", "note": "Adjustment for overpayment in May due to mid-month policy change", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "reporting_month": "2025-05-01" } ] } } ``` ### Scenario 3: Employees Joining a Group Policy When employees join mid-period, their first contribution report typically contains multiple contributions: pro-rated amounts for the partial initial period and full amounts for subsequent complete 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 pro-rated nature of the contribution #### How Pro-rata Works Pro-rated amounts appear as regular contributions with shorter coverage periods: * **Coverage period**: Clearly indicated in the `cover_period` field with specific `from_date` and `to_date` * **Calculation**: Already handled by the insurer based on the partial coverage period #### Implementation Considerations When processing contributions for new employees, your system should: 1. **Apply the correct amount** to the appropriate period 2. **Maintain clear records** of the employee's join date and initial coverage 3. **Handle both 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`** ```json Example { "object": "contribution_report_employee_breakdown", "employee_id": "ee_c245fde199004eee845b2c26a5732ce2", "contribution_report_id": "ctr_7d37c383d2bf49c4beea3ebdbe4de787", "employer_id": "er_01227f212421474cbe687cde68568d37", "external_customer_id": null, "period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "currency": "eur", "status": "finalized", "finalized_at": "2025-06-01T00:00:10.311966Z", "created_at": "2025-05-10T20:53:14.196315Z", "last_updated_at": "2025-06-01T00:00:10.323195Z", "health_insurance": { "employer_contributions": [ { "id": "ct_0447692a1bf74400965b1c1b00f33610", "category": "gross_premium", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 83.33, "type": "recurring", "note": "Pro-rated for employee joining April 20th", "cover_period": { "from_date": "2025-04-20", "to_date": "2025-04-30" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-04-01" }, { "id": "ct_8db2eb4b9d9446a0b9fef29a7f3c36c9", "category": "gross_premium", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 250.00, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-05-01" } ] } } ``` ### 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 correct covered portion * **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. Refunds for departed employees are identified by: * The same `employee_id` and `employee_policy_id` as the original contribution * Marked with `adjustment=true` and a reference via `adjustment_for` * Negative amounts for refunds * Often include explanatory notes mentioning the departure #### Implementation Considerations When processing refunds for departed employees, your system should: 1. **Identify departure refunds** by checking the `adjustment` flag and negative amounts 2. **Link the refund** to the original contribution using the `adjustment_for` reference 3. **Consider applying the refund to the employer's account** rather than the departed employee (depending on your platform's refund policy) 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. **`Node.js`** ```javascript Node.js // Process refund for an employee who has left async function processEmployeeLeaverRefund(breakdown) { const { employee_id, health_insurance } = breakdown; if (!health_insurance?.employer_contributions) return; // Find refund adjustments (negative amounts with adjustment flag) const refundAdjustments = health_insurance.employer_contributions.filter( contribution => contribution.adjustment && contribution.amount < 0 ); for (const refund of refundAdjustments) { // Link to the original contribution being adjusted const originalContributionId = refund.adjustment_for; // Process the refund in your system await processRefund(employee_id, refund, originalContributionId); } } // Implementation for processing refunds async function processRefund(employeeId, refund, originalContributionId) { // Apply the refund to your system (e.g., credit employer account) await applyRefundToAccount(employeeId, refund.amount); // Record the refund with link to original contribution for audit trail await recordRefund({ employeeId: employeeId, refundId: refund.id, amount: refund.amount, originalContributionId: originalContributionId, coveragePeriod: refund.cover_period, note: refund.note }); } ``` **`Example`** ```json Example { "object": "contribution_report_employee_breakdown", "employee_id": "ee_c245fde199004eee845b2c26a5732ce2", "contribution_report_id": "ctr_7d37c383d2bf49c4beea3ebdbe4de787", "employer_id": "er_01227f212421474cbe687cde68568d37", "external_customer_id": null, "period": { "from_date": "2025-06-01", "to_date": "2025-06-30" }, "currency": "eur", "status": "finalized", "finalized_at": "2025-07-01T00:00:10.311966Z", "created_at": "2025-06-10T20:53:14.196315Z", "last_updated_at": "2025-07-01T00:00:10.323195Z", "health_insurance": { "employer_contributions": [ { "id": "ct_8db2eb4b9d9446a0b9fef29a7f3c36c9", "category": "gross_premium", "member_type": "policyholder", "adjustment": true, "adjustment_for": "ct_0447692a1bf74400965b1c1b00f33610", "amount": -166.67, "type": "recurring", "note": "Refund for employee leaving on May 10th", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-10" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-05-01" } ] } } ``` ### Scenario 5: Working with tax relief - Irish Tax Relief at Source (TRS) example In Ireland, health insurance contributions may include Tax Relief at Source (TRS), which creates separate contribution entries for premium and tax relief amounts. This scenario requires special handling to ensure correct payroll processing and compliance with Irish tax regulations. #### Understanding TRS Contributions TRS contributions appear as separate entries in the contribution report. The handling differs significantly depending on whether it's an employer or employee contribution. #### TRS Data Structure For Irish health insurance with TRS, you will receive separate contribution entries. The following provides a simplified overview of the TRS handling, please ensure your implementation complies with current Irish tax regulations and Revenue requirements. **Employer contributions with TRS:** * **Premium contribution**: Gross premium amount with `category: "gross_premium"` * **Tax relief contribution**: TRS amount with `category: "tax_relief"` * **Payment flow**: Employer sends TRS amount to Revenue and pays discounted amount to insurer * **Employee claim**: Employee can claim the TRS amount at year-end tax filing **Employee contributions with TRS:** * **Premium contribution**: Gross premium amount with `category: "gross_premium"` * **Tax relief contribution**: TRS amount with `category: "tax_relief"` * **Payment flow**: Only the discounted amount is relevant for the employee #### Implementation Considerations When processing TRS contributions, your system should: 1. **Identify TRS entries** by checking for `category: "tax_relief"` 2. **Handle premium and tax relief entries** according to their different requirements 3. **Handle employer vs employee contributions differently** as they have distinct payment flows 4. **Maintain records** of both gross and net amounts for compliance **`Node.js`** ```javascript Node.js // Process TRS contributions - identify premium and tax relief pairs async function processTRSContributions(breakdown) { const { employee_id, health_insurance } = breakdown; if (!health_insurance) return; for (const contributionSet of [health_insurance.employer_contributions, health_insurance.employee_contributions]) { if (!contributionSet) continue; const grossPremiumContributions = contributionSet.filter(c => c.category === 'gross_premium'); const trsContributions = contributionSet.filter(c => c.category === 'tax_relief'); for (const grossPremium of grossPremiumContributions) { // Find matching TRS contribution const matchingTrs = trsContributions.find(trs => trs.member_type === grossPremium.member_type && trs.employee_policy_id === grossPremium.employee_policy_id ); // Process according to your system's requirements await processTRSPair({ grossPremium: grossPremium, trs: matchingTrs, employeeId: employee_id }); } } } ``` **`Example`** ```json Example { "object": "contribution_report_employee_breakdown", "employee_id": "ee_c245fde199004eee845b2c26a5732ce2", "contribution_report_id": "ctr_7d37c383d2bf49c4beea3ebdbe4de787", "employer_id": "er_01227f212421474cbe687cde68568d37", "external_customer_id": null, "period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "currency": "eur", "status": "finalized", "finalized_at": "2025-06-01T00:00:10.311966Z", "created_at": "2025-05-10T20:53:14.196315Z", "last_updated_at": "2025-06-01T00:00:10.323195Z", "health_insurance": { "employer_contributions": [ { "id": "ct_0447692a1bf74400965b1c1b00f33610", "category": "gross_premium", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 100.0, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-05-01" }, { "id": "ct_8db2eb4b9d9446a0b9fef29a7f3c36c9", "category": "tax_relief", "member_type": "policyholder", "adjustment": false, "adjustment_for": null, "amount": 20.0, "type": "recurring", "note": "TRS", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-05-01" } ], "employee_contributions": [ { "id": "ct_a468afd6d35242a4af2239d014804018", "category": "gross_premium", "member_type": "partner_dependant", "adjustment": false, "adjustment_for": null, "amount": 100.0, "type": "recurring", "note": "", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-05-01" }, { "id": "ct_c0d990c990d34ae09ab8970a0bb921ef", "category": "tax_relief", "member_type": "partner_dependant", "adjustment": false, "adjustment_for": null, "amount": 20.0, "type": "recurring", "note": "TRS", "cover_period": { "from_date": "2025-05-01", "to_date": "2025-05-31" }, "employee_policy_id": "eehp_fa505f9505d3464cbf7cabd5d763722a", "reporting_month": "2025-05-01" } ] } } ``` ### Scenario 6: EoR Platform Processing Employer of Record (EoR) platforms handle multiple customers under a single employer relationship. For these platforms, contribution reports are automatically separated by customer, with each customer receiving their own dedicated report identified by the `external_customer_id` field. #### Understanding EoR Report Structure For EoR platforms, the system creates separate contribution reports for each customer: * **Separate reports per customer**: Each `external_customer_id` gets its own contribution report, even if they share the same employer * **Customer-specific filtering**: You can filter reports by `external_customer_id` when retrieving contribution reports * **Consistent identification**: The same `external_customer_id` appears in both the contribution report and all associated employee breakdowns #### Processing EoR Contribution Reports When processing reports as an EoR platform, you should: 1. **Filter by customer**: Use the `external_customer_id` parameter when listing reports to process specific customers 2. **Maintain customer separation**: Keep contributions separated by customer in your accounting system **`Node.js`** ```javascript Node.js // EoR platform: Process contributions for a specific customer async function processEoRCustomerContributions(customerId) { // Get all contribution reports for this specific customer const response = await kotaClient.get('/contribution_reports', { params: { external_customer_id: customerId, status: 'finalized' } }); const reports = response.data.data; for (const report of reports) { console.log(`Processing report ${report.id} for customer ${report.external_customer_id}`); // Get employee breakdowns for this customer's report const breakdowns = await getEmployeeBreakdowns(report.id); // Process each employee's contributions for this customer for (const breakdown of breakdowns) { await processCustomerEmployeeContributions(breakdown, customerId); } } } // Process employee contributions with customer context async function processCustomerEmployeeContributions(breakdown, customerId) { const { employee_id, health_insurance, external_customer_id } = breakdown; // Process contributions with customer context if (health_insurance) { await processCustomerContributions(customerId, employee_id, health_insurance.employer_contributions, 'employer'); await processCustomerContributions(customerId, employee_id, health_insurance.employee_contributions, 'employee'); } } ``` --- > Understanding Contribution Reporting ## API Docs - Contribution Reports [Retrieve a contribution report](https://docs.kota.io/api-reference/contribution-reports/retrieve-contribution-report.md) - Contribution Reports [List all contribution reports](https://docs.kota.io/api-reference/contribution-reports/list-contribution-reports.md) - Contribution Reports [Finalize a contribution report](https://docs.kota.io/api-reference/contribution-reports/finalize-contribution-report.md) - Contribution Reports [Retrieve an employee breakdown](https://docs.kota.io/api-reference/contribution-reports/retrieve-employee-breakdown-for-contribution-report.md) - Contribution Reports [List all employee breakdowns](https://docs.kota.io/api-reference/contribution-reports/list-employee-breakdowns-for-contribution-report.md) ## OpenAPI Specification The raw OpenAPI 3.1 specification for this API is available at: - [OpenAPI JSON](https://docs.kota.io/openapi.json) - [OpenAPI YAML](https://docs.kota.io/openapi.yaml)