Payroll API
API reference
Base URL https://api.payroll.merot.com
Calculations
Single and batch payroll calculations, gross/net conversion and proration.
Calculate one employee's payroll
# POST /v1/calculations
Either grossSalary (gross-first — the usual case under MK labour law) or targetNet (net-first — the agreed net is inverted to the statutory gross that produces it) is required. See MK payroll concepts for when to use each.
Parameters
X-Company-Id (header)stringRequired when your key has access to more than one employer company (accounting-firm and vendor keys).
Request body
grossSalarystring | nullA decimal denar amount as a string, never a JSON number.
targetNetstring | nullA decimal denar amount as a string, never a JSON number.
netBasedbooleanPrice special hours off the net hourly instead of the gross hourly, then re-derive the statutory gross (net-terms contracts).
Default:
falsecontractorbooleanFlat personal income tax, no social contributions, no personal allowance (договор за дело).
Default:
falseworkingHoursnumberContracted monthly hours; defaults to the fund of hours (176).
seniorityPercentagenumber | nullSeniority allowance — a percentage of the base gross salary, applied directly.
seniorityobject | nullAlternative to `seniorityPercentage` — computed from months of service as of a date, at a rate per completed year.
Show child parameters
monthsintegerasOfstringratePerYearnumberDefault:
0.5periodYearinteger | nullperiodMonthinteger | null
overtimeHoursnumber | nullovertimeRatenumber | nullTotal % of the base hourly; defaults to 135 (base + 35% supplement).
nightHoursnumber | nullnightHoursRatenumber | nullDefaults to 135.
holidayHoursnumber | nullholidayHoursRatenumber | nullDefaults to 150.
sundayHoursnumber | nullsundayHoursRatenumber | nullDefaults to 150.
holidayNightHoursnumber | nullCombined holiday+night premium hours.
holidayNightRatenumber | nullDefaults to 185.
sundayNightHoursnumber | nullsundayNightRatenumber | nullDefaults to 185.
sundayHolidayHoursnumber | nullsundayHolidayRatenumber | nullDefaults to 200.
sickFundHoursnumber | nullSick leave paid by ФZOM (the health fund) — hours only; the fund pays the employee directly.
sickFundRatenumber | nullDefaults to 70 (% of the base hourly).
sickCompanyHoursnumber | nullSick leave paid by the employer.
sickCompanyRatenumber | nullDefaults to 70.
shift1Hoursnumber | nullshift1Ratenumber | nullDefaults to 100.
shift2Hoursnumber | nullshift2Ratenumber | nullDefaults to 100.
shift3Hoursnumber | nullshift3Ratenumber | nullDefaults to 135.
specialHoursnumber | nullspecialRatenumber | nullDefaults to 100.
unpaidHoursnumber | nullabsentHoursnumber | nullHours outside the employment window in a partial month (unpaid, no compensation) — see also `period`/`placements`.
maternityHoursnumber | nullMaternity-leave hours paid by ФЗОМ, carved out of the base at no employer cost.
fundHoursnumber | nullThe full month's fund of hours, when `workingHours` is a part-time figure — gives the ratio used for the minimum contribution base and the personal allowance.
minBaseModeenum | nullWhen to top up contributions to the legal minimum base (50% of the average salary): only on partial months (default), always, or never.
proratedalwaysoffperiodobject | nullAlternative to `absentHours` — the target month; combine with `placements` to have Merot compute the proration for you.
Show child parameters
yearintegermonthinteger
placementsarray of object | nullShow child parameters
startDatestringendDatestring | null
hoursInformationalbooleanRecord the premium hours without changing gross/net — the contracted salary already covers them.
Default:
falseseniorityInformationalbooleanRecord the seniority percentage without adding it to gross/net.
Default:
falsebonusAmountstring | nullA decimal denar amount as a string, never a JSON number.
bonusBasisenum"net" (default): net rises by exactly bonusAmount, gross is inverted to match. "gross": bonusAmount is added to gross, net follows.
grossnetDefault:
"net"pitExemptbooleanDefault:
falsenoPersonalAllowancebooleanDrop the personal allowance (the employee claims it at a different, primary employer).
Default:
falsepersonalAllowancestring | nullA decimal denar amount as a string, never a JSON number.
averageSalarystring | nullA decimal denar amount as a string, never a JSON number.
deductionsarray of object | nullPost-net deductions (insurance, union dues, court order, ...) — reduce netPaid only, never gross or contributions.
Show child parameters
descriptionstringrequiredamountstringrequiredA decimal denar amount as a string, never a JSON number.
rateOverridesobject | nullAdvanced — override individual statutory rate keys (see `GET /v1/rates`) for this calculation only.
effectiveDatestring | nullRates in force on this date; defaults to now.
Returns
- 200
The calculation result.
- 422
validation_failed — one or more fields failed validation.
### POST /v1/calculations
Calculate one employee's payroll
Either `grossSalary` (gross-first — the usual case under MK labour law) or `targetNet`
(net-first — the agreed net is inverted to the statutory gross that produces it) is
required. See [MK payroll concepts](/payroll/concepts) for when to use each.
```bash
curl -X POST "https://api.payroll.merot.com/v1/calculations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"grossSalary": "45000",
"workingHours": 176,
"overtimeHours": 8,
"nightHours": 4
}'
```
**Example response**
```json
{
"salaryGross": "49142",
"baseGross": "45000",
"pension": "9779",
"health": "3686",
"employmentContrib": "49",
"additionalHealth": "246",
"totalContributions": "13760",
"pensionTopUp": "0",
"healthTopUp": "0",
"disabilityTopUp": "0",
"employmentTopUp": "0",
"personalAllowance": "10932",
"taxableIncome": "24450",
"pit": "2445",
"salaryNet": "32937",
"deductionsTotal": "0",
"netPaid": "32937",
"workingHours": 176,
"seniorityPercentage": 0,
"seniority": "0",
"bonusAmount": "0",
"bonusBasis": "net",
"overtimeHours": 8,
"overtimePay": "2761",
"nightHours": 4,
"nightPay": "1381",
"holidayHours": 0,
"holidayPay": "0",
"sundayHours": 0,
"sundayPay": "0",
"sickHours": 0,
"sickRate": 70,
"sickPay": "0",
"sickFundHours": 0,
"sickFundRate": 70,
"sickFundPay": "0",
"sickCompanyHours": 0,
"sickCompanyRate": 70,
"sickCompanyPay": "0",
"shift1Hours": 0,
"shift1Rate": 100,
"shift1Pay": "0",
"shift2Hours": 0,
"shift2Rate": 100,
"shift2Pay": "0",
"shift3Hours": 0,
"shift3Rate": 135,
"shift3Pay": "0",
"specialHours": 0,
"specialRate": 100,
"specialPay": "0",
"unpaidHours": 0,
"absentHours": 0,
"maternityHours": 0,
"holidayNightHours": 0,
"holidayNightRate": 185,
"holidayNightPay": "0",
"sundayNightHours": 0,
"sundayNightRate": 185,
"sundayNightPay": "0",
"sundayHolidayHours": 0,
"sundayHolidayRate": 200,
"sundayHolidayPay": "0"
}
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/calculations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"grossSalary": "45000",
"workingHours": 176,
"overtimeHours": 8,
"nightHours": 4
}' const response = await fetch("https://api.payroll.merot.com/v1/calculations", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"grossSalary": "45000",
"workingHours": 176,
"overtimeHours": 8,
"nightHours": 4
}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/calculations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"grossSalary": "45000",
"workingHours": 176,
"overtimeHours": 8,
"nightHours": 4
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/calculations', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'grossSalary' => '45000',
'workingHours' => 176,
'overtimeHours' => 8,
'nightHours' => 4,
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/calculations");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{
""grossSalary"": ""45000"",
""workingHours"": 176,
""overtimeHours"": 8,
""nightHours"": 4
}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/calculations"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{ \"grossSalary\": \"45000\", \"workingHours\": 176, \"overtimeHours\": 8, \"nightHours\": 4 }"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"salaryGross": "49142",
"baseGross": "45000",
"pension": "9779",
"health": "3686",
"employmentContrib": "49",
"additionalHealth": "246",
"totalContributions": "13760",
"pensionTopUp": "0",
"healthTopUp": "0",
"disabilityTopUp": "0",
"employmentTopUp": "0",
"personalAllowance": "10932",
"taxableIncome": "24450",
"pit": "2445",
"salaryNet": "32937",
"deductionsTotal": "0",
"netPaid": "32937",
"workingHours": 176,
"seniorityPercentage": 0,
"seniority": "0",
"bonusAmount": "0",
"bonusBasis": "net",
"overtimeHours": 8,
"overtimePay": "2761",
"nightHours": 4,
"nightPay": "1381",
"holidayHours": 0,
"holidayPay": "0",
"sundayHours": 0,
"sundayPay": "0",
"sickHours": 0,
"sickRate": 70,
"sickPay": "0",
"sickFundHours": 0,
"sickFundRate": 70,
"sickFundPay": "0",
"sickCompanyHours": 0,
"sickCompanyRate": 70,
"sickCompanyPay": "0",
"shift1Hours": 0,
"shift1Rate": 100,
"shift1Pay": "0",
"shift2Hours": 0,
"shift2Rate": 100,
"shift2Pay": "0",
"shift3Hours": 0,
"shift3Rate": 135,
"shift3Pay": "0",
"specialHours": 0,
"specialRate": 100,
"specialPay": "0",
"unpaidHours": 0,
"absentHours": 0,
"maternityHours": 0,
"holidayNightHours": 0,
"holidayNightRate": 185,
"holidayNightPay": "0",
"sundayNightHours": 0,
"sundayNightRate": 185,
"sundayNightPay": "0",
"sundayHolidayHours": 0,
"sundayHolidayRate": 200,
"sundayHolidayPay": "0"
} Calculate payroll for up to 500 employees
# POST /v1/calculations/batch
Parameters
X-Company-Id (header)stringRequired when your key has access to more than one employer company (accounting-firm and vendor keys).
Request body
inputsarray of objectrequiredShow child parameters
grossSalarystring | nullA decimal denar amount as a string, never a JSON number.
targetNetstring | nullA decimal denar amount as a string, never a JSON number.
netBasedbooleanPrice special hours off the net hourly instead of the gross hourly, then re-derive the statutory gross (net-terms contracts).
Default:
falsecontractorbooleanFlat personal income tax, no social contributions, no personal allowance (договор за дело).
Default:
falseworkingHoursnumberContracted monthly hours; defaults to the fund of hours (176).
seniorityPercentagenumber | nullSeniority allowance — a percentage of the base gross salary, applied directly.
seniorityobject | nullAlternative to `seniorityPercentage` — computed from months of service as of a date, at a rate per completed year.
Show child parameters
monthsintegerasOfstringratePerYearnumberDefault:
0.5periodYearinteger | nullperiodMonthinteger | null
overtimeHoursnumber | nullovertimeRatenumber | nullTotal % of the base hourly; defaults to 135 (base + 35% supplement).
nightHoursnumber | nullnightHoursRatenumber | nullDefaults to 135.
holidayHoursnumber | nullholidayHoursRatenumber | nullDefaults to 150.
sundayHoursnumber | nullsundayHoursRatenumber | nullDefaults to 150.
holidayNightHoursnumber | nullCombined holiday+night premium hours.
holidayNightRatenumber | nullDefaults to 185.
sundayNightHoursnumber | nullsundayNightRatenumber | nullDefaults to 185.
sundayHolidayHoursnumber | nullsundayHolidayRatenumber | nullDefaults to 200.
sickFundHoursnumber | nullSick leave paid by ФZOM (the health fund) — hours only; the fund pays the employee directly.
sickFundRatenumber | nullDefaults to 70 (% of the base hourly).
sickCompanyHoursnumber | nullSick leave paid by the employer.
sickCompanyRatenumber | nullDefaults to 70.
shift1Hoursnumber | nullshift1Ratenumber | nullDefaults to 100.
shift2Hoursnumber | nullshift2Ratenumber | nullDefaults to 100.
shift3Hoursnumber | nullshift3Ratenumber | nullDefaults to 135.
specialHoursnumber | nullspecialRatenumber | nullDefaults to 100.
unpaidHoursnumber | nullabsentHoursnumber | nullHours outside the employment window in a partial month (unpaid, no compensation) — see also `period`/`placements`.
maternityHoursnumber | nullMaternity-leave hours paid by ФЗОМ, carved out of the base at no employer cost.
fundHoursnumber | nullThe full month's fund of hours, when `workingHours` is a part-time figure — gives the ratio used for the minimum contribution base and the personal allowance.
minBaseModeenum | nullWhen to top up contributions to the legal minimum base (50% of the average salary): only on partial months (default), always, or never.
proratedalwaysoffperiodobject | nullAlternative to `absentHours` — the target month; combine with `placements` to have Merot compute the proration for you.
Show child parameters
yearintegermonthinteger
placementsarray of object | nullShow child parameters
startDatestringendDatestring | null
hoursInformationalbooleanRecord the premium hours without changing gross/net — the contracted salary already covers them.
Default:
falseseniorityInformationalbooleanRecord the seniority percentage without adding it to gross/net.
Default:
falsebonusAmountstring | nullA decimal denar amount as a string, never a JSON number.
bonusBasisenum"net" (default): net rises by exactly bonusAmount, gross is inverted to match. "gross": bonusAmount is added to gross, net follows.
grossnetDefault:
"net"pitExemptbooleanDefault:
falsenoPersonalAllowancebooleanDrop the personal allowance (the employee claims it at a different, primary employer).
Default:
falsepersonalAllowancestring | nullA decimal denar amount as a string, never a JSON number.
averageSalarystring | nullA decimal denar amount as a string, never a JSON number.
deductionsarray of object | nullPost-net deductions (insurance, union dues, court order, ...) — reduce netPaid only, never gross or contributions.
Show child parameters
descriptionstringrequiredamountstringrequiredA decimal denar amount as a string, never a JSON number.
rateOverridesobject | nullAdvanced — override individual statutory rate keys (see `GET /v1/rates`) for this calculation only.
effectiveDatestring | nullRates in force on this date; defaults to now.
Returns
- 200
One result per input, in the same order.
- 422
validation_failed — one or more fields failed validation.
### POST /v1/calculations/batch
Calculate payroll for up to 500 employees
```bash
curl -X POST "https://api.payroll.merot.com/v1/calculations/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/calculations/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/calculations/batch", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/calculations/batch",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/calculations/batch', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/calculations/batch");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/calculations/batch"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); Convert between agreed net and statutory gross
# POST /v1/calculations/convert
The base-salary conversion with no special hours, seniority or bonus — the same inversion targetNet uses internally on POST /v1/calculations, exposed standalone for salary-entry screens. Give either gross or net.
Request body
grossstringA decimal denar amount as a string, never a JSON number.
netstringA decimal denar amount as a string, never a JSON number.
contractorbooleanDefault:
falserateOverridesobjectAdvanced — override individual statutory rate keys (see `GET /v1/rates`) for this calculation only.
effectiveDatestringRates in force on this date; defaults to now.
Returns
- 200
Both figures, denar-exact.
### POST /v1/calculations/convert
Convert between agreed net and statutory gross
The base-salary conversion with no special hours, seniority or bonus — the same inversion
`targetNet` uses internally on `POST /v1/calculations`, exposed standalone for salary-entry
screens. Give either `gross` or `net`.
```bash
curl -X POST "https://api.payroll.merot.com/v1/calculations/convert" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"net": "35000.00"
}'
```
**Example response**
```json
{
"gross": "52324.00",
"net": "35000.00"
}
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/calculations/convert" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"net": "35000.00"
}' const response = await fetch("https://api.payroll.merot.com/v1/calculations/convert", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"net": "35000.00"
}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/calculations/convert",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"net": "35000.00"
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/calculations/convert', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'net' => '35000.00',
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/calculations/convert");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{
""net"": ""35000.00""
}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/calculations/convert"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{ \"net\": \"35000.00\" }"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"gross": "52324.00",
"net": "35000.00"
} Prorate placement windows against a calendar month
# POST /v1/calculations/prorate
Given one or more employment windows (placements) and a target month, returns the service days, paid/absent hours and hire/leave day-of-month — everything a partial-month МПИН filing needs (МПИН fields 3.5, 3.6, 3.27, 3.28) — without calculating any money. POST /v1/calculations can do this in one step via its own period/placements fields; use this endpoint when you only need the day/hour math.
Request body
yearintegerrequiredmonthintegerrequiredfundHoursnumberrequiredThe month's full fund of hours (e.g. 176).
placementsarray of objectrequiredShow child parameters
startDatestringrequiredendDatestring | null
Returns
- 200
Proration result.
### POST /v1/calculations/prorate
Prorate placement windows against a calendar month
Given one or more employment windows (`placements`) and a target month, returns the
service days, paid/absent hours and hire/leave day-of-month — everything a partial-month
МПИН filing needs (МПИН fields 3.5, 3.6, 3.27, 3.28) — without calculating any money.
`POST /v1/calculations` can do this in one step via its own `period`/`placements` fields;
use this endpoint when you only need the day/hour math.
```bash
curl -X POST "https://api.payroll.merot.com/v1/calculations/prorate" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"year": 2026,
"month": 9,
"fundHours": 176,
"placements": [
{
"startDate": "2026-09-19",
"endDate": null
}
]
}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/calculations/prorate" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"year": 2026,
"month": 9,
"fundHours": 176,
"placements": [
{
"startDate": "2026-09-19",
"endDate": null
}
]
}' const response = await fetch("https://api.payroll.merot.com/v1/calculations/prorate", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"year": 2026,
"month": 9,
"fundHours": 176,
"placements": [
{
"startDate": "2026-09-19",
"endDate": null
}
]
}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/calculations/prorate",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"year": 2026,
"month": 9,
"fundHours": 176,
"placements": [
{
"startDate": "2026-09-19",
"endDate": None
}
]
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/calculations/prorate', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'year' => 2026,
'month' => 9,
'fundHours' => 176,
'placements' => [
[
'startDate' => '2026-09-19',
'endDate' => null,
],
],
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/calculations/prorate");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{
""year"": 2026,
""month"": 9,
""fundHours"": 176,
""placements"": [
{
""startDate"": ""2026-09-19"",
""endDate"": null
}
]
}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/calculations/prorate"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{ \"year\": 2026, \"month\": 9, \"fundHours\": 176, \"placements\": [ { \"startDate\": \"2026-09-19\", \"endDate\": null } ] }"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); Rates
Current and historical statutory contribution and tax rates.
Endpoints
### GET /v1/rates
Current MK statutory rates
```bash
curl -X GET "https://api.payroll.merot.com/v1/rates" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Example response**
```json
{
"pension_rate": "0.199",
"health_rate": "0.075",
"disability_rate": "0.005",
"employment_rate": "0.001",
"pit_rate": "0.1",
"personal_allowance": "10932",
"average_salary": "69141",
"max_contribution_multiplier": "16",
"min_contribution_base_ratio": "0.5",
"default_working_hours": "176",
"overtime_rate": "135",
"night_hours_rate": "135",
"holiday_hours_rate": "150",
"sunday_hours_rate": "150"
}
``` cURL
curl -X GET "https://api.payroll.merot.com/v1/rates" \
-H "Authorization: Bearer YOUR_API_KEY" const response = await fetch("https://api.payroll.merot.com/v1/rates", {
method: "GET",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
},
});
const data = await response.json();
console.log(data); import requests
response = requests.get(
"https://api.payroll.merot.com/v1/rates",
headers={
"Authorization": "Bearer YOUR_API_KEY",
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.payroll.merot.com/v1/rates', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.payroll.merot.com/v1/rates");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/rates"))
.headers("Authorization", "Bearer YOUR_API_KEY")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"pension_rate": "0.199",
"health_rate": "0.075",
"disability_rate": "0.005",
"employment_rate": "0.001",
"pit_rate": "0.1",
"personal_allowance": "10932",
"average_salary": "69141",
"max_contribution_multiplier": "16",
"min_contribution_base_ratio": "0.5",
"default_working_hours": "176",
"overtime_rate": "135",
"night_hours_rate": "135",
"holiday_hours_rate": "150",
"sunday_hours_rate": "150"
} Rate change history
# GET /v1/rates/history
Every effective-dated version of every rate key, most recent first.
Returns
- 200
Rate history grouped by key.
### GET /v1/rates/history
Rate change history
Every effective-dated version of every rate key, most recent first.
```bash
curl -X GET "https://api.payroll.merot.com/v1/rates/history" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Example response**
```json
{
"history": {
"pension_rate": [
{
"rateValue": "0.199",
"effectiveFrom": "2026-07-01",
"effectiveTo": null,
"description": "Сл. весник 148/2026 — ПИО 18.8% → 19.9%"
},
{
"rateValue": "0.188",
"effectiveFrom": "2020-01-01",
"effectiveTo": "2026-06-30",
"description": null
}
]
}
}
``` cURL
curl -X GET "https://api.payroll.merot.com/v1/rates/history" \
-H "Authorization: Bearer YOUR_API_KEY" const response = await fetch("https://api.payroll.merot.com/v1/rates/history", {
method: "GET",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
},
});
const data = await response.json();
console.log(data); import requests
response = requests.get(
"https://api.payroll.merot.com/v1/rates/history",
headers={
"Authorization": "Bearer YOUR_API_KEY",
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.payroll.merot.com/v1/rates/history', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.payroll.merot.com/v1/rates/history");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/rates/history"))
.headers("Authorization", "Bearer YOUR_API_KEY")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"history": {
"pension_rate": [
{
"rateValue": "0.199",
"effectiveFrom": "2026-07-01",
"effectiveTo": null,
"description": "Сл. весник 148/2026 — ПИО 18.8% → 19.9%"
},
{
"rateValue": "0.188",
"effectiveFrom": "2020-01-01",
"effectiveTo": "2026-06-30",
"description": null
}
]
}
} Leave
Annual-leave accrual, year-end carry-over and termination cash-out.
Entitled and accrued annual-leave days
# POST /v1/leave/accrual
MK ЗРО (Labour Relations Law) чл. 139–140.
Request body
hireDatestringrequiredasOfDatestringrequiredemploymentTypeenumfull_timecontractorDefault:
"full_time"partTimePercentnumberDefault:
100careerSeniorityYearsnumberDefault:
0under18booleanDefault:
falseeffectiveDatestring | null
Returns
- 200
Accrual result.
### POST /v1/leave/accrual
Entitled and accrued annual-leave days
MK ЗРО (Labour Relations Law) чл. 139–140.
```bash
curl -X POST "https://api.payroll.merot.com/v1/leave/accrual" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"hireDate": "2024-03-01",
"asOfDate": "2026-09-26",
"employmentType": "full_time",
"partTimePercent": 100,
"careerSeniorityYears": 6,
"under18": false
}'
```
**Example response**
```json
{
"entitledDays": 22,
"accruedDays": 22,
"vested": true,
"seniorityBonusDays": 2,
"nextAccrualDate": "2026-10-01",
"notes": [],
"effectiveDate": "2026-09-26T00:00:00.000Z"
}
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/leave/accrual" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"hireDate": "2024-03-01",
"asOfDate": "2026-09-26",
"employmentType": "full_time",
"partTimePercent": 100,
"careerSeniorityYears": 6,
"under18": false
}' const response = await fetch("https://api.payroll.merot.com/v1/leave/accrual", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"hireDate": "2024-03-01",
"asOfDate": "2026-09-26",
"employmentType": "full_time",
"partTimePercent": 100,
"careerSeniorityYears": 6,
"under18": false
}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/leave/accrual",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"hireDate": "2024-03-01",
"asOfDate": "2026-09-26",
"employmentType": "full_time",
"partTimePercent": 100,
"careerSeniorityYears": 6,
"under18": False
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/leave/accrual', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'hireDate' => '2024-03-01',
'asOfDate' => '2026-09-26',
'employmentType' => 'full_time',
'partTimePercent' => 100,
'careerSeniorityYears' => 6,
'under18' => false,
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/leave/accrual");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{
""hireDate"": ""2024-03-01"",
""asOfDate"": ""2026-09-26"",
""employmentType"": ""full_time"",
""partTimePercent"": 100,
""careerSeniorityYears"": 6,
""under18"": false
}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/leave/accrual"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{ \"hireDate\": \"2024-03-01\", \"asOfDate\": \"2026-09-26\", \"employmentType\": \"full_time\", \"partTimePercent\": 100, \"careerSeniorityYears\": 6, \"under18\": false }"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"entitledDays": 22,
"accruedDays": 22,
"vested": true,
"seniorityBonusDays": 2,
"nextAccrualDate": "2026-10-01",
"notes": [],
"effectiveDate": "2026-09-26T00:00:00.000Z"
} Year-end carry-over and forfeiture
# POST /v1/leave/carry-over
Request body
yearintegerrequiredbalanceRemainingAtYearEndnumberrequiredemployerPreventedbooleanTrue lifts the carry-over cap (the employer, not the employee, prevented the leave from being used).
Default:
falseeffectiveDatestring
Returns
- 200
Carry-over result.
### POST /v1/leave/carry-over
Year-end carry-over and forfeiture
```bash
curl -X POST "https://api.payroll.merot.com/v1/leave/carry-over" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"year": 2026,
"balanceRemainingAtYearEnd": 8,
"employerPrevented": false
}'
```
**Example response**
```json
{
"carriedInDays": 8,
"forfeitedDays": 0,
"deadline": "2027-06-30",
"effectiveDate": "2026-09-26T00:00:00.000Z"
}
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/leave/carry-over" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"year": 2026,
"balanceRemainingAtYearEnd": 8,
"employerPrevented": false
}' const response = await fetch("https://api.payroll.merot.com/v1/leave/carry-over", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"year": 2026,
"balanceRemainingAtYearEnd": 8,
"employerPrevented": false
}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/leave/carry-over",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"year": 2026,
"balanceRemainingAtYearEnd": 8,
"employerPrevented": False
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/leave/carry-over', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'year' => 2026,
'balanceRemainingAtYearEnd' => 8,
'employerPrevented' => false,
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/leave/carry-over");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{
""year"": 2026,
""balanceRemainingAtYearEnd"": 8,
""employerPrevented"": false
}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/leave/carry-over"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{ \"year\": 2026, \"balanceRemainingAtYearEnd\": 8, \"employerPrevented\": false }"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"carriedInDays": 8,
"forfeitedDays": 0,
"deadline": "2027-06-30",
"effectiveDate": "2026-09-26T00:00:00.000Z"
} Unused annual-leave cash-out on termination
# POST /v1/leave/termination-cash-out
MK ЗРО чл. 145 — unused days × average daily gross over the preceding 12 months.
Request body
remainingDaysnumberrequiredlast12MonthsGrossarray of stringrequiredterminationDatestringrequiredcurrencystringrequiredeffectiveDatestring
Returns
- 200
Cash-out amount and line item.
### POST /v1/leave/termination-cash-out
Unused annual-leave cash-out on termination
MK ЗРО чл. 145 — unused days × average daily gross over the preceding 12 months.
```bash
curl -X POST "https://api.payroll.merot.com/v1/leave/termination-cash-out" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/leave/termination-cash-out" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/leave/termination-cash-out", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/leave/termination-cash-out",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/leave/termination-cash-out', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/leave/termination-cash-out");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/leave/termination-cash-out"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); Files
МПИН, е-ППД and ПДД-ГИ file generation for upload to UJP.
Generate a МПИН XML file
# POST /v1/files/mpin/xml
МПИН (MPIN) is UJP's monthly wage-and-contributions filing. This returns the XML file for one legal entity and period, built from the batch and per-employee records you provide. By default the file is checked against the same UJP-style rules POST /v1/files/mpin/validate runs; blocking issues fail the request with 422 — pass force: true to generate anyway.
Request body
companyobjectrequiredShow child parameters
taxIdstring | nullregistrationNumberstring | nullnamestringrequirednameCyrstring | nulladdressstring | nulladdressStreetstring | nulladdressNumberstring | nulladdressAreastring | nullpostalCodestring | nullfaxstring | nullcitystring | nullmunicipalityCodestring | nullmanagerNamestring | nullphonestring | nullemailstring | nullareaCodestring | nullМПИН 2.3 — derived from `city` when absent.
fzomBranchstring | nullМПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.
batchobjectrequiredShow child parameters
periodMonthintegerrequiredperiodYearintegerrequiredworkingHoursnumberThe month's fund of hours; falls back to 176 for any record that carries none.
Default:
176vidObvrskastring | nullМПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.
vidObvrznikstring | nullМПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).
filingVariantenum | nullAdvanced, rarely needed: `no_payment_declared` (internal code 555) files the same calculation with the paid-net field reported as 0.00 — used when contributions must be declared before the salary itself is paid out. `vid_700` reproduces the вид на обврска 700 filing UJP accepted for one payroll run in 08/2026; its exact meaning beyond that is not documented by UJP and this option is offered as-is.
regularno_payment_declaredvid_700
recordsarray of objectrequiredShow child parameters
salaryGrossstringrequiredA decimal denar amount as a string, never a JSON number.
salaryNetstringrequiredA decimal denar amount as a string, never a JSON number.
pensionstringrequiredA decimal denar amount as a string, never a JSON number.
healthstringrequiredA decimal denar amount as a string, never a JSON number.
additionalHealthstringrequiredA decimal denar amount as a string, never a JSON number.
employmentContribstringrequiredA decimal denar amount as a string, never a JSON number.
pitstringrequiredA decimal denar amount as a string, never a JSON number.
personalAllowancestringA decimal denar amount as a string, never a JSON number.
overtimeHoursnumberDefault:
0overtimePaystringA decimal denar amount as a string, never a JSON number.
nightHoursnumberDefault:
0nightPaystringA decimal denar amount as a string, never a JSON number.
holidayHoursnumberDefault:
0holidayPaystringA decimal denar amount as a string, never a JSON number.
sundayHoursnumberDefault:
0sundayPaystringA decimal denar amount as a string, never a JSON number.
workingHoursnumber | nullDefaults to the batch's `workingHours`.
sickFundHoursnumber | nullsickFundRatenumber | nullsickFundPaystringA decimal denar amount as a string, never a JSON number.
sickCompanyHoursnumber | nullsickCompanyRatenumber | nullsickCompanyPaystringA decimal denar amount as a string, never a JSON number.
unpaidHoursnumber | nullabsentHoursnumber | nullМПИН 3.5 — hours outside the placement in a partial month.
serviceDaysnumber | nullpensionTopUpstringA decimal denar amount as a string, never a JSON number.
healthTopUpstringA decimal denar amount as a string, never a JSON number.
disabilityTopUpstringA decimal denar amount as a string, never a JSON number.
employmentTopUpstringA decimal denar amount as a string, never a JSON number.
hireDayinteger | nullМПИН 3.27 — day of month hired, if inside the period.
leaveDayinteger | nullМПИН 3.28 — day of month left, if inside the period.
contractNointeger | nullМПИН 3.30 — 2+ for a re-hire within the same month.
insuranceCodestring | nullМПИН 3.17 — вид на стаж (kind of insured service).
sickFundCodestring | nullsickFundBasestringA decimal denar amount as a string, never a JSON number.
maternityHoursnumber | nullМПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).
maternityBasestringA decimal denar amount as a string, never a JSON number.
maternityPartialHoursnumber | nullHours worked after an early return from maternity leave (field 3.23 = 129).
clientTaxIdstring | nullnetPaidstringA decimal denar amount as a string, never a JSON number.
employeeobjectrequiredShow child parameters
embgstring | nullЕМБГ — the employee's unique master citizen number.
firstNamestringrequiredlastNamestringrequiredfirstNameCyrstring | nulllastNameCyrstring | nullbankAccountstring | nullmunicipalityCodestring | nullfzomBranchstring | null
forcebooleanGenerate even if validation found blocking issues.
Default:
false
Returns
- 200
The МПИН XML file.
- 422
mpin_validation_failed — blocking issues; the response lists them (or pass
force: true).
### POST /v1/files/mpin/xml
Generate a МПИН XML file
МПИН (MPIN) is UJP's monthly wage-and-contributions filing. This returns the XML file for
one legal entity and period, built from the batch and per-employee records you provide.
By default the file is checked against the same UJP-style rules `POST /v1/files/mpin/validate`
runs; blocking issues fail the request with 422 — pass `force: true` to generate anyway.
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/xml" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/xml" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/files/mpin/xml", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/mpin/xml",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/mpin/xml', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/mpin/xml");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/mpin/xml"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); application/xml — binary/file response, no JSON body to preview.
Generate a МПИН TXT file
# POST /v1/files/mpin/txt
The legacy fixed-width TXT format UJP's own client software also accepts, encoded exactly as UJP expects it: UTF-16LE with a byte-order mark, CRLF line endings.
Request body
companyobjectrequiredShow child parameters
taxIdstring | nullregistrationNumberstring | nullnamestringrequirednameCyrstring | nulladdressstring | nulladdressStreetstring | nulladdressNumberstring | nulladdressAreastring | nullpostalCodestring | nullfaxstring | nullcitystring | nullmunicipalityCodestring | nullmanagerNamestring | nullphonestring | nullemailstring | nullareaCodestring | nullМПИН 2.3 — derived from `city` when absent.
fzomBranchstring | nullМПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.
batchobjectrequiredShow child parameters
periodMonthintegerrequiredperiodYearintegerrequiredworkingHoursnumberThe month's fund of hours; falls back to 176 for any record that carries none.
Default:
176vidObvrskastring | nullМПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.
vidObvrznikstring | nullМПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).
filingVariantenum | nullAdvanced, rarely needed: `no_payment_declared` (internal code 555) files the same calculation with the paid-net field reported as 0.00 — used when contributions must be declared before the salary itself is paid out. `vid_700` reproduces the вид на обврска 700 filing UJP accepted for one payroll run in 08/2026; its exact meaning beyond that is not documented by UJP and this option is offered as-is.
regularno_payment_declaredvid_700
recordsarray of objectrequiredShow child parameters
salaryGrossstringrequiredA decimal denar amount as a string, never a JSON number.
salaryNetstringrequiredA decimal denar amount as a string, never a JSON number.
pensionstringrequiredA decimal denar amount as a string, never a JSON number.
healthstringrequiredA decimal denar amount as a string, never a JSON number.
additionalHealthstringrequiredA decimal denar amount as a string, never a JSON number.
employmentContribstringrequiredA decimal denar amount as a string, never a JSON number.
pitstringrequiredA decimal denar amount as a string, never a JSON number.
personalAllowancestringA decimal denar amount as a string, never a JSON number.
overtimeHoursnumberDefault:
0overtimePaystringA decimal denar amount as a string, never a JSON number.
nightHoursnumberDefault:
0nightPaystringA decimal denar amount as a string, never a JSON number.
holidayHoursnumberDefault:
0holidayPaystringA decimal denar amount as a string, never a JSON number.
sundayHoursnumberDefault:
0sundayPaystringA decimal denar amount as a string, never a JSON number.
workingHoursnumber | nullDefaults to the batch's `workingHours`.
sickFundHoursnumber | nullsickFundRatenumber | nullsickFundPaystringA decimal denar amount as a string, never a JSON number.
sickCompanyHoursnumber | nullsickCompanyRatenumber | nullsickCompanyPaystringA decimal denar amount as a string, never a JSON number.
unpaidHoursnumber | nullabsentHoursnumber | nullМПИН 3.5 — hours outside the placement in a partial month.
serviceDaysnumber | nullpensionTopUpstringA decimal denar amount as a string, never a JSON number.
healthTopUpstringA decimal denar amount as a string, never a JSON number.
disabilityTopUpstringA decimal denar amount as a string, never a JSON number.
employmentTopUpstringA decimal denar amount as a string, never a JSON number.
hireDayinteger | nullМПИН 3.27 — day of month hired, if inside the period.
leaveDayinteger | nullМПИН 3.28 — day of month left, if inside the period.
contractNointeger | nullМПИН 3.30 — 2+ for a re-hire within the same month.
insuranceCodestring | nullМПИН 3.17 — вид на стаж (kind of insured service).
sickFundCodestring | nullsickFundBasestringA decimal denar amount as a string, never a JSON number.
maternityHoursnumber | nullМПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).
maternityBasestringA decimal denar amount as a string, never a JSON number.
maternityPartialHoursnumber | nullHours worked after an early return from maternity leave (field 3.23 = 129).
clientTaxIdstring | nullnetPaidstringA decimal denar amount as a string, never a JSON number.
employeeobjectrequiredShow child parameters
embgstring | nullЕМБГ — the employee's unique master citizen number.
firstNamestringrequiredlastNamestringrequiredfirstNameCyrstring | nulllastNameCyrstring | nullbankAccountstring | nullmunicipalityCodestring | nullfzomBranchstring | null
forcebooleanGenerate even if validation found blocking issues.
Default:
false
Returns
- 200
The МПИН TXT file (
text/plain; charset=utf-16le). - 422
mpin_validation_failed
### POST /v1/files/mpin/txt
Generate a МПИН TXT file
The legacy fixed-width TXT format UJP's own client software also accepts, encoded exactly
as UJP expects it: UTF-16LE with a byte-order mark, CRLF line endings.
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/txt" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/txt" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/files/mpin/txt", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/mpin/txt",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/mpin/txt', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/mpin/txt");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/mpin/txt"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); text/plain — binary/file response, no JSON body to preview.
Generate one combined МПИН TXT for several batches
# POST /v1/files/mpin/txt/combined
One filing for several payroll batches of the same legal entity and period (e.g. regular pay plus a correction run), records sorted by Cyrillic surname as UJP expects.
Request body
companyobjectrequiredShow child parameters
taxIdstring | nullregistrationNumberstring | nullnamestringrequirednameCyrstring | nulladdressstring | nulladdressStreetstring | nulladdressNumberstring | nulladdressAreastring | nullpostalCodestring | nullfaxstring | nullcitystring | nullmunicipalityCodestring | nullmanagerNamestring | nullphonestring | nullemailstring | nullareaCodestring | nullМПИН 2.3 — derived from `city` when absent.
fzomBranchstring | nullМПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.
batchobjectrequiredShow child parameters
periodMonthintegerrequiredperiodYearintegerrequiredworkingHoursnumberThe month's fund of hours; falls back to 176 for any record that carries none.
Default:
176vidObvrskastring | nullМПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.
vidObvrznikstring | nullМПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).
filingVariantenum | nullAdvanced, rarely needed: `no_payment_declared` (internal code 555) files the same calculation with the paid-net field reported as 0.00 — used when contributions must be declared before the salary itself is paid out. `vid_700` reproduces the вид на обврска 700 filing UJP accepted for one payroll run in 08/2026; its exact meaning beyond that is not documented by UJP and this option is offered as-is.
regularno_payment_declaredvid_700
batchesarray of objectrequiredShow child parameters
recordsarray of objectShow child parameters
salaryGrossstringrequiredA decimal denar amount as a string, never a JSON number.
salaryNetstringrequiredA decimal denar amount as a string, never a JSON number.
pensionstringrequiredA decimal denar amount as a string, never a JSON number.
healthstringrequiredA decimal denar amount as a string, never a JSON number.
additionalHealthstringrequiredA decimal denar amount as a string, never a JSON number.
employmentContribstringrequiredA decimal denar amount as a string, never a JSON number.
pitstringrequiredA decimal denar amount as a string, never a JSON number.
personalAllowancestringA decimal denar amount as a string, never a JSON number.
overtimeHoursnumberDefault:
0overtimePaystringA decimal denar amount as a string, never a JSON number.
nightHoursnumberDefault:
0nightPaystringA decimal denar amount as a string, never a JSON number.
holidayHoursnumberDefault:
0holidayPaystringA decimal denar amount as a string, never a JSON number.
sundayHoursnumberDefault:
0sundayPaystringA decimal denar amount as a string, never a JSON number.
workingHoursnumber | nullDefaults to the batch's `workingHours`.
sickFundHoursnumber | nullsickFundRatenumber | nullsickFundPaystringA decimal denar amount as a string, never a JSON number.
sickCompanyHoursnumber | nullsickCompanyRatenumber | nullsickCompanyPaystringA decimal denar amount as a string, never a JSON number.
unpaidHoursnumber | nullabsentHoursnumber | nullМПИН 3.5 — hours outside the placement in a partial month.
serviceDaysnumber | nullpensionTopUpstringA decimal denar amount as a string, never a JSON number.
healthTopUpstringA decimal denar amount as a string, never a JSON number.
disabilityTopUpstringA decimal denar amount as a string, never a JSON number.
employmentTopUpstringA decimal denar amount as a string, never a JSON number.
hireDayinteger | nullМПИН 3.27 — day of month hired, if inside the period.
leaveDayinteger | nullМПИН 3.28 — day of month left, if inside the period.
contractNointeger | nullМПИН 3.30 — 2+ for a re-hire within the same month.
insuranceCodestring | nullМПИН 3.17 — вид на стаж (kind of insured service).
sickFundCodestring | nullsickFundBasestringA decimal denar amount as a string, never a JSON number.
maternityHoursnumber | nullМПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).
maternityBasestringA decimal denar amount as a string, never a JSON number.
maternityPartialHoursnumber | nullHours worked after an early return from maternity leave (field 3.23 = 129).
clientTaxIdstring | nullnetPaidstringA decimal denar amount as a string, never a JSON number.
employeeobjectrequiredShow child parameters
embgstring | nullЕМБГ — the employee's unique master citizen number.
firstNamestringrequiredlastNamestringrequiredfirstNameCyrstring | nulllastNameCyrstring | nullbankAccountstring | nullmunicipalityCodestring | nullfzomBranchstring | null
Returns
- 200
The combined МПИН TXT file.
### POST /v1/files/mpin/txt/combined
Generate one combined МПИН TXT for several batches
One filing for several payroll batches of the same legal entity and period (e.g. regular pay plus a correction run), records sorted by Cyrillic surname as UJP expects.
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/txt/combined" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/txt/combined" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/files/mpin/txt/combined", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/mpin/txt/combined",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/mpin/txt/combined', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/mpin/txt/combined");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/mpin/txt/combined"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); text/plain — binary/file response, no JSON body to preview.
Run UJP-style checks without generating a file
# POST /v1/files/mpin/validate
Request body
companyobjectrequiredShow child parameters
taxIdstring | nullregistrationNumberstring | nullnamestringrequirednameCyrstring | nulladdressstring | nulladdressStreetstring | nulladdressNumberstring | nulladdressAreastring | nullpostalCodestring | nullfaxstring | nullcitystring | nullmunicipalityCodestring | nullmanagerNamestring | nullphonestring | nullemailstring | nullareaCodestring | nullМПИН 2.3 — derived from `city` when absent.
fzomBranchstring | nullМПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.
batchobjectrequiredShow child parameters
periodMonthintegerrequiredperiodYearintegerrequiredworkingHoursnumberThe month's fund of hours; falls back to 176 for any record that carries none.
Default:
176vidObvrskastring | nullМПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.
vidObvrznikstring | nullМПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).
filingVariantenum | nullAdvanced, rarely needed: `no_payment_declared` (internal code 555) files the same calculation with the paid-net field reported as 0.00 — used when contributions must be declared before the salary itself is paid out. `vid_700` reproduces the вид на обврска 700 filing UJP accepted for one payroll run in 08/2026; its exact meaning beyond that is not documented by UJP and this option is offered as-is.
regularno_payment_declaredvid_700
recordsarray of objectrequiredShow child parameters
salaryGrossstringrequiredA decimal denar amount as a string, never a JSON number.
salaryNetstringrequiredA decimal denar amount as a string, never a JSON number.
pensionstringrequiredA decimal denar amount as a string, never a JSON number.
healthstringrequiredA decimal denar amount as a string, never a JSON number.
additionalHealthstringrequiredA decimal denar amount as a string, never a JSON number.
employmentContribstringrequiredA decimal denar amount as a string, never a JSON number.
pitstringrequiredA decimal denar amount as a string, never a JSON number.
personalAllowancestringA decimal denar amount as a string, never a JSON number.
overtimeHoursnumberDefault:
0overtimePaystringA decimal denar amount as a string, never a JSON number.
nightHoursnumberDefault:
0nightPaystringA decimal denar amount as a string, never a JSON number.
holidayHoursnumberDefault:
0holidayPaystringA decimal denar amount as a string, never a JSON number.
sundayHoursnumberDefault:
0sundayPaystringA decimal denar amount as a string, never a JSON number.
workingHoursnumber | nullDefaults to the batch's `workingHours`.
sickFundHoursnumber | nullsickFundRatenumber | nullsickFundPaystringA decimal denar amount as a string, never a JSON number.
sickCompanyHoursnumber | nullsickCompanyRatenumber | nullsickCompanyPaystringA decimal denar amount as a string, never a JSON number.
unpaidHoursnumber | nullabsentHoursnumber | nullМПИН 3.5 — hours outside the placement in a partial month.
serviceDaysnumber | nullpensionTopUpstringA decimal denar amount as a string, never a JSON number.
healthTopUpstringA decimal denar amount as a string, never a JSON number.
disabilityTopUpstringA decimal denar amount as a string, never a JSON number.
employmentTopUpstringA decimal denar amount as a string, never a JSON number.
hireDayinteger | nullМПИН 3.27 — day of month hired, if inside the period.
leaveDayinteger | nullМПИН 3.28 — day of month left, if inside the period.
contractNointeger | nullМПИН 3.30 — 2+ for a re-hire within the same month.
insuranceCodestring | nullМПИН 3.17 — вид на стаж (kind of insured service).
sickFundCodestring | nullsickFundBasestringA decimal denar amount as a string, never a JSON number.
maternityHoursnumber | nullМПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).
maternityBasestringA decimal denar amount as a string, never a JSON number.
maternityPartialHoursnumber | nullHours worked after an early return from maternity leave (field 3.23 = 129).
clientTaxIdstring | nullnetPaidstringA decimal denar amount as a string, never a JSON number.
employeeobjectrequiredShow child parameters
embgstring | nullЕМБГ — the employee's unique master citizen number.
firstNamestringrequiredlastNamestringrequiredfirstNameCyrstring | nulllastNameCyrstring | nullbankAccountstring | nullmunicipalityCodestring | nullfzomBranchstring | null
forcebooleanGenerate even if validation found blocking issues.
Default:
false
Returns
- 200
Validation result.
### POST /v1/files/mpin/validate
Run UJP-style checks without generating a file
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/validate" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
```
**Example response**
```json
{
"ok": true,
"issues": []
}
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/mpin/validate" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/files/mpin/validate", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/mpin/validate",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/mpin/validate', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/mpin/validate");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/mpin/validate"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"ok": true,
"issues": []
} Generate a ПДД-ГИ XML file for contractor payments
# POST /v1/files/pddgi
ПДД-ГИ (PDD-GI) reports personal income tax withheld on contractor/honorarium payments made from a payroll batch.
Request body
companyobjectrequiredShow child parameters
taxIdstring | nullnamestringrequiredemailstring | nullphonestring | nullcontactPersonstring | nullmunicipalityCodestring | null
monthintegerrequiredyearintegerrequiredrecordsarray of objectrequiredShow child parameters
salaryGrossstringrequiredA decimal denar amount as a string, never a JSON number.
employeeobjectrequiredShow child parameters
embgstring | nullfirstNamestringrequiredlastNamestringrequiredbankAccountstring | null
Returns
- 200
The ПДД-ГИ XML file.
### POST /v1/files/pddgi
Generate a ПДД-ГИ XML file for contractor payments
ПДД-ГИ (PDD-GI) reports personal income tax withheld on contractor/honorarium payments made from a payroll batch.
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/pddgi" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/pddgi" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/files/pddgi", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/pddgi",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/pddgi', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/pddgi");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/pddgi"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); application/xml — binary/file response, no JSON body to preview.
Generate a standalone е-ППД ГИ XML file
# POST /v1/files/epdd
е-ППД (e-PPD) reports personal income tax on a payment outside the regular payroll batch (ujp_ppf_acc_calc).
Request body
issuerobjectrequiredShow child parameters
edbstringrequirednamestringrequiredbankAccountstring | nullemailstring | nullphonestring | null
periodFromstringrequiredperiodTostringrequiredcommentstring | nullrowsarray of objectrequiredShow child parameters
embgstringrequiredfirstNamestringrequiredlastNamestringrequiredmunicipalityCodestring | nullsubtypeIncomestring | nullgrossIncomestringrequiredA decimal denar amount as a string, never a JSON number.
bankAccountstring | null
Returns
- 200
The е-ППД XML file.
### POST /v1/files/epdd
Generate a standalone е-ППД ГИ XML file
е-ППД (e-PPD) reports personal income tax on a payment outside the regular payroll batch (`ujp_ppf_acc_calc`).
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/epdd" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/epdd" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/files/epdd", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/epdd",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/epdd', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/epdd");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/epdd"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); application/xml — binary/file response, no JSON body to preview.
Gross-up a net payment for е-ППД (flat 10% PIT)
# POST /v1/files/epdd/gross-from-net
Request body
netstringrequiredA decimal denar amount as a string, never a JSON number.
Returns
- 200
Gross amount.
### POST /v1/files/epdd/gross-from-net
Gross-up a net payment for е-ППД (flat 10% PIT)
```bash
curl -X POST "https://api.payroll.merot.com/v1/files/epdd/gross-from-net" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"net": "20000"
}'
```
**Example response**
```json
{
"net": "20000",
"gross": "22222"
}
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/files/epdd/gross-from-net" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"net": "20000"
}' const response = await fetch("https://api.payroll.merot.com/v1/files/epdd/gross-from-net", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"net": "20000"
}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/files/epdd/gross-from-net",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"net": "20000"
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/files/epdd/gross-from-net', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'net' => '20000',
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/files/epdd/gross-from-net");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{
""net"": ""20000""
}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/files/epdd/gross-from-net"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{ \"net\": \"20000\" }"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"net": "20000",
"gross": "22222"
} Payslips
Payslip PDF generation in MK, SQ and EN.
Endpoints
Generate a payslip PDF
# POST /v1/payslips
Renders a payslip PDF from a calculation result (from POST /v1/calculations or your own batch), the employee/company details and a language. Returns the PDF directly.
Request body
companyobjectrequiredShow child parameters
namestringrequiredtaxIdstring | nulladdressstring | null
employeeobjectrequiredShow child parameters
firstNamestringrequiredlastNamestringrequiredembgstring | nullpositionstring | null
periodobjectrequiredShow child parameters
yearintegerrequiredmonthintegerrequired
calculationobjectrequiredShow child parameters
salaryGrossstringA decimal denar amount as a string, never a JSON number.
baseGrossstringA decimal denar amount as a string, never a JSON number.
pensionstringA decimal denar amount as a string, never a JSON number.
healthstringA decimal denar amount as a string, never a JSON number.
employmentContribstringA decimal denar amount as a string, never a JSON number.
additionalHealthstringA decimal denar amount as a string, never a JSON number.
totalContributionsstringA decimal denar amount as a string, never a JSON number.
pensionTopUpstringA decimal denar amount as a string, never a JSON number.
healthTopUpstringA decimal denar amount as a string, never a JSON number.
disabilityTopUpstringA decimal denar amount as a string, never a JSON number.
employmentTopUpstringA decimal denar amount as a string, never a JSON number.
personalAllowancestringA decimal denar amount as a string, never a JSON number.
taxableIncomestringA decimal denar amount as a string, never a JSON number.
pitstringA decimal denar amount as a string, never a JSON number.
salaryNetstringA decimal denar amount as a string, never a JSON number.
deductionsTotalstringA decimal denar amount as a string, never a JSON number.
netPaidstringA decimal denar amount as a string, never a JSON number.
workingHoursnumberseniorityPercentagenumbersenioritystringA decimal denar amount as a string, never a JSON number.
bonusAmountstringA decimal denar amount as a string, never a JSON number.
bonusBasisenumgrossnetovertimeHoursnumberovertimePaystringA decimal denar amount as a string, never a JSON number.
nightHoursnumbernightPaystringA decimal denar amount as a string, never a JSON number.
holidayHoursnumberholidayPaystringA decimal denar amount as a string, never a JSON number.
sundayHoursnumbersundayPaystringA decimal denar amount as a string, never a JSON number.
sickHoursnumbersickRatenumbersickPaystringA decimal denar amount as a string, never a JSON number.
sickFundHoursnumbersickFundRatenumbersickFundPaystringA decimal denar amount as a string, never a JSON number.
sickCompanyHoursnumbersickCompanyRatenumbersickCompanyPaystringA decimal denar amount as a string, never a JSON number.
shift1Hoursnumbershift1Ratenumbershift1PaystringA decimal denar amount as a string, never a JSON number.
shift2Hoursnumbershift2Ratenumbershift2PaystringA decimal denar amount as a string, never a JSON number.
shift3Hoursnumbershift3Ratenumbershift3PaystringA decimal denar amount as a string, never a JSON number.
specialHoursnumberspecialRatenumberspecialPaystringA decimal denar amount as a string, never a JSON number.
unpaidHoursnumberabsentHoursnumbermaternityHoursnumberholidayNightHoursnumberholidayNightRatenumberholidayNightPaystringA decimal denar amount as a string, never a JSON number.
sundayNightHoursnumbersundayNightRatenumbersundayNightPaystringA decimal denar amount as a string, never a JSON number.
sundayHolidayHoursnumbersundayHolidayRatenumbersundayHolidayPaystringA decimal denar amount as a string, never a JSON number.
prorationobject | nullPresent only when the request used `period` + `placements`.
Show child parameters
serviceDaysnumber | nullabsentHoursnumberpaidHoursnumber | nullhireDayinteger | nullleaveDayinteger | null
languageenumrequiredmksqenDefault:
"mk"
Returns
- 200
The payslip PDF.
- 422
validation_failed — one or more fields failed validation.
### POST /v1/payslips
Generate a payslip PDF
Renders a payslip PDF from a calculation result (from `POST /v1/calculations` or your own
batch), the employee/company details and a language. Returns the PDF directly.
```bash
curl -X POST "https://api.payroll.merot.com/v1/payslips" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
``` cURL
curl -X POST "https://api.payroll.merot.com/v1/payslips" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' const response = await fetch("https://api.payroll.merot.com/v1/payslips", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const data = await response.json();
console.log(data); import requests
response = requests.post(
"https://api.payroll.merot.com/v1/payslips",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.payroll.merot.com/v1/payslips', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.payroll.merot.com/v1/payslips");
request.Headers.Add("Authorization", "Bearer YOUR_API_KEY");
var json = /* lang=json */ @"{}";
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/payslips"))
.headers("Authorization", "Bearer YOUR_API_KEY", "Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(/* JSON */ "{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); application/pdf — binary/file response, no JSON body to preview.
Health
Public status endpoint (unauthenticated).
Endpoints
### GET /v1/health
Service status
```bash
curl -X GET "https://api.payroll.merot.com/v1/health"
```
**Example response**
```json
{
"status": "ok",
"version": "2026-09-26"
}
``` cURL
curl -X GET "https://api.payroll.merot.com/v1/health" const response = await fetch("https://api.payroll.merot.com/v1/health", {
method: "GET",
headers: {
},
});
const data = await response.json();
console.log(data); import requests
response = requests.get(
"https://api.payroll.merot.com/v1/health",
headers={
},
)
print(response.json()) <?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.payroll.merot.com/v1/health', [
'headers' => [
],
]);
echo $response->getBody(); using System.Net.Http;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.payroll.merot.com/v1/health");
using var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body); import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.payroll.merot.com/v1/health"))
.headers()
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); {
"status": "ok",
"version": "2026-09-26"
}