> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.kota.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.kota.io/_mcp/server.

# Finalize a contribution report

POST https://test.api.kota.io/contribution_reports/{contribution_report_id}/finalize

This endpoint finalizes the `contribution report` object, making it immutable and preventing any further updates. Once finalized, its `status` changes to `finalized`, and it serves as a historical record for payroll reporting. Any changes or adjustments that occur after the finalization will roll into the next reporting period.

Reference: https://docs.kota.io/api-reference/contribution-reports/finalize-contribution-report

## Authentication

- `Authorization` header (bearer token, required) — Authorization header using the Bearer scheme

## Servers

- `https://test.api.kota.io` (test, default)
- `https://api.kota.io` (production)

## Request

### Path parameters

- `contribution_report_id` (string, required)

### Headers

- `X-Platform-Id` (string, optional) — The target platform id. Required only when calling with a dashboard (WorkOS AuthKit) access token instead of a platform API key — the token carries no platform claim, so the caller must say which platform it means. Ignored for platform API key / embed session token callers.

## Response

### 200

OK

- `id` (string, required) — Unique identifier for the contribution report
- `employer_id` (string, required) — Unique identifier of the employer for which the report is created
- `period` (GetContributionReportPeriod, required) — Period covered by the contribution report
- `status` (enum, required) — Current status of the contribution report
  - Allowed values: `open`, `finalized`
- `created_at` (datetime, required) — Date and time the report was created
- `last_updated_at` (datetime, required) — Date and time of the last update to the report
- `aggregates` (ContributionReportAggregates, required) — Summary figures for the report, meant for dashboards and overviews. They keep changing while the report is open. For payroll processing or reconciliation use the employee breakdowns, which carry the itemised contributions.
- `object` (string, optional) — The object type
- `external_customer_id` (string, optional, nullable) — Unique identifier of the customer for which the report is created. This identifier is assigned by the Employer of Record (EoR) platform to the Customer (i.e. the company using the EoR service) for which the employee is employed. This parameter is only available to EoR platforms.
- `finalized_at` (datetime, optional, nullable) — Date and time the report was finalized, if applicable
- `file_id` (string, optional, nullable) — Identifier of the report's downloadable CSV file. Null until the CSV has been prepared, which happens shortly after the report is finalized — use `file_status` to tell whether it is still on its way. Exchange it for a download link via `GET /files/{file_id}/download_link`.
- `file_status` (enum, optional) — Whether the report's downloadable CSV is ready yet. `pending`: still being prepared. Re-fetch the report; it normally becomes available within seconds of finalization. `ready`: available, and `file_id` is set. `failed`: preparation did not succeed and is not retried automatically, so further polling will not help. Contact support to have it prepared again. Null while the report is open, because a CSV is only prepared once a report is finalized.
  - Allowed values: `pending`, `ready`, `failed`

## Errors

### 400 Bad Request Error

Bad Request

- `type` (string, optional, nullable)
- `title` (string, optional, nullable)
- `status` (integer, optional, nullable)
- `detail` (string, optional, nullable)
- `instance` (string, optional, nullable)

### 404 Not Found Error

Not Found

- `type` (string, optional, nullable)
- `title` (string, optional, nullable)
- `status` (integer, optional, nullable)
- `detail` (string, optional, nullable)
- `instance` (string, optional, nullable)

## Types

### GetContributionReportPeriod

- `from_date` (date, required) — Start date of the period
- `to_date` (date, required) — End date of the period

### ContributionReportAggregates

- `employee_count` (integer, required) — Number of employees with at least one contribution in the report
- `totals` (list of ContributionReportTotals, required) — Contribution totals, one entry per currency. Often a single entry; empty when the report has no contributions yet.

### ContributionReportTotals

- `currency` (enum, required) — The currency of the amounts in this entry (e.g. `gbp`)
  - Allowed values: `eur`, `aed`, `afn`, `xcd`, `all`, `amd`, `aoa`, `ars`, `usd`, `aud`, `awg`, `azn`, `bam`, `bbd`, `bdt`, `xof`, `bgn`, `bhd`, `bif`, `bmd`, `bnd`, `bob`, `bov`, `brl`, `bsd`, `inr`, `btn`, `nok`, `bwp`, `byn`, `bzd`, `cad`, `xaf`, `cdf`, `chf`, `che`, `chw`, `nzd`, `clp`, `clf`, `cny`, `cop`, `cou`, `crc`, `cup`, `cuc`, `cve`, `ang`, `czk`, `djf`, `dkk`, `dop`, `dzd`, `egp`, `mad`, `ern`, `etb`, `fjd`, `fkp`, `mdl`, `gbp`, `gel`, `ghs`, `gip`, `gmd`, `gnf`, `gtq`, `gyd`, `hkd`, `hnl`, `hrk`, `htg`, `huf`, `idr`, `xdr`, `ils`, `iqd`, `irr`, `isk`, `jmd`, `jod`, `jpy`, `kes`, `kgs`, `khr`, `kmf`, `kpw`, `krw`, `kwd`, `kyd`, `kzt`, `lak`, `lbp`, `lkr`, `lrd`, `lsl`, `zar`, `lyd`, `mga`, `mkd`, `mmk`, `mnt`, `mop`, `mru`, `mur`, `mvr`, `mwk`, `mxn`, `mxv`, `myr`, `mzn`, `nad`, `xpf`, `ngn`, `nio`, `npr`, `omr`, `pab`, `pen`, `pgk`, `php`, `pkr`, `pln`, `pyg`, `qar`, `ron`, `rsd`, `rub`, `rwf`, `sar`, `sbd`, `scr`, `sdg`, `sek`, `sgd`, `shp`, `sll`, `sos`, `srd`, `ssp`, `stn`, `svc`, `xsu`, `syp`, `twd`, `szl`, `thb`, `tjs`, `tmt`, `tnd`, `top`, `try`, `ttd`, `tzs`, `uah`, `ugx`, `usn`, `uyu`, `uyi`, `uyw`, `uzs`, `ves`, `vnd`, `vuv`, `wst`, `yer`, `xua`, `zmw`, `zwl`
- `total_amount` (double, required) — Sum of `employer_amount` and `employee_amount`
- `employer_amount` (double, required) — Total paid by the employer for the benefits in this report, adjustments included
- `employee_amount` (double, required) — Total paid by the employees for the benefits in this report, adjustments included

## Examples

**Response**

```json
{
  "id": "ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a",
  "employer_id": "er_3b1333d87d9d4fd6ad83ba7f6b0e951a",
  "period": {
    "from_date": "2024-12-01",
    "to_date": "2024-12-01"
  },
  "status": "open",
  "created_at": "2024-12-01T00:00:00Z",
  "last_updated_at": "2024-12-01T00:00:00Z",
  "aggregates": {
    "employee_count": 123,
    "totals": [
      {
        "currency": "eur",
        "total_amount": 123.45,
        "employer_amount": 123.45,
        "employee_amount": 123.45
      }
    ]
  },
  "object": "string",
  "external_customer_id": null,
  "finalized_at": "2024-12-01T00:00:00Z",
  "file_id": "file_3b1333d87d9d4fd6ad83ba7f6b0e951a",
  "file_status": null
}
```

**SDK Code**

```python
import requests

url = "https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize"

headers = {"Authorization": "Bearer <apiKey>"}

response = requests.post(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize';
const options = {method: 'POST', headers: {Authorization: 'Bearer <apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "Bearer <apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <apiKey>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize")
  .header("Authorization", "Bearer <apiKey>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize', [
  'headers' => [
    'Authorization' => 'Bearer <apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <apiKey>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://test.api.kota.io/contribution_reports/ctr_3b1333d87d9d4fd6ad83ba7f6b0e951a/finalize")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```