Payroll API
v1 Guides MK Get started

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)string

    Required when your key has access to more than one employer company (accounting-firm and vendor keys).

Request body

  • grossSalarystring | null

    A decimal denar amount as a string, never a JSON number.

  • targetNetstring | null

    A decimal denar amount as a string, never a JSON number.

  • netBasedboolean

    Price special hours off the net hourly instead of the gross hourly, then re-derive the statutory gross (net-terms contracts).

    Default: false

  • contractorboolean

    Flat personal income tax, no social contributions, no personal allowance (договор за дело).

    Default: false

  • workingHoursnumber

    Contracted monthly hours; defaults to the fund of hours (176).

  • seniorityPercentagenumber | null

    Seniority allowance — a percentage of the base gross salary, applied directly.

  • seniorityobject | null

    Alternative to `seniorityPercentage` — computed from months of service as of a date, at a rate per completed year.

    Show child parametersHide child parameters
    • monthsinteger
    • asOfstring
    • ratePerYearnumber

      Default: 0.5

    • periodYearinteger | null
    • periodMonthinteger | null
  • overtimeHoursnumber | null
  • overtimeRatenumber | null

    Total % of the base hourly; defaults to 135 (base + 35% supplement).

  • nightHoursnumber | null
  • nightHoursRatenumber | null

    Defaults to 135.

  • holidayHoursnumber | null
  • holidayHoursRatenumber | null

    Defaults to 150.

  • sundayHoursnumber | null
  • sundayHoursRatenumber | null

    Defaults to 150.

  • holidayNightHoursnumber | null

    Combined holiday+night premium hours.

  • holidayNightRatenumber | null

    Defaults to 185.

  • sundayNightHoursnumber | null
  • sundayNightRatenumber | null

    Defaults to 185.

  • sundayHolidayHoursnumber | null
  • sundayHolidayRatenumber | null

    Defaults to 200.

  • sickFundHoursnumber | null

    Sick leave paid by ФZOM (the health fund) — hours only; the fund pays the employee directly.

  • sickFundRatenumber | null

    Defaults to 70 (% of the base hourly).

  • sickCompanyHoursnumber | null

    Sick leave paid by the employer.

  • sickCompanyRatenumber | null

    Defaults to 70.

  • shift1Hoursnumber | null
  • shift1Ratenumber | null

    Defaults to 100.

  • shift2Hoursnumber | null
  • shift2Ratenumber | null

    Defaults to 100.

  • shift3Hoursnumber | null
  • shift3Ratenumber | null

    Defaults to 135.

  • specialHoursnumber | null
  • specialRatenumber | null

    Defaults to 100.

  • unpaidHoursnumber | null
  • absentHoursnumber | null

    Hours outside the employment window in a partial month (unpaid, no compensation) — see also `period`/`placements`.

  • maternityHoursnumber | null

    Maternity-leave hours paid by ФЗОМ, carved out of the base at no employer cost.

  • fundHoursnumber | null

    The 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 | null

    When to top up contributions to the legal minimum base (50% of the average salary): only on partial months (default), always, or never.

    proratedalwaysoff
  • periodobject | null

    Alternative to `absentHours` — the target month; combine with `placements` to have Merot compute the proration for you.

    Show child parametersHide child parameters
    • yearinteger
    • monthinteger
  • placementsarray of object | null
    Show child parametersHide child parameters
    • startDatestring
    • endDatestring | null
  • hoursInformationalboolean

    Record the premium hours without changing gross/net — the contracted salary already covers them.

    Default: false

  • seniorityInformationalboolean

    Record the seniority percentage without adding it to gross/net.

    Default: false

  • bonusAmountstring | null

    A 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.

    grossnet

    Default: "net"

  • pitExemptboolean

    Default: false

  • noPersonalAllowanceboolean

    Drop the personal allowance (the employee claims it at a different, primary employer).

    Default: false

  • personalAllowancestring | null

    A decimal denar amount as a string, never a JSON number.

  • averageSalarystring | null

    A decimal denar amount as a string, never a JSON number.

  • deductionsarray of object | null

    Post-net deductions (insurance, union dues, court order, ...) — reduce netPaid only, never gross or contributions.

    Show child parametersHide child parameters
    • descriptionstringrequired
    • amountstringrequired

      A decimal denar amount as a string, never a JSON number.

  • rateOverridesobject | null

    Advanced — override individual statutory rate keys (see `GET /v1/rates`) for this calculation only.

  • effectiveDatestring | null

    Rates in force on this date; defaults to now.

Returns

  • 200

    The calculation result.

  • 422

    validation_failed — one or more fields failed validation.

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());
Response
{
  "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)string

    Required when your key has access to more than one employer company (accounting-firm and vendor keys).

Request body

  • inputsarray of objectrequired
    Show child parametersHide child parameters
    • grossSalarystring | null

      A decimal denar amount as a string, never a JSON number.

    • targetNetstring | null

      A decimal denar amount as a string, never a JSON number.

    • netBasedboolean

      Price special hours off the net hourly instead of the gross hourly, then re-derive the statutory gross (net-terms contracts).

      Default: false

    • contractorboolean

      Flat personal income tax, no social contributions, no personal allowance (договор за дело).

      Default: false

    • workingHoursnumber

      Contracted monthly hours; defaults to the fund of hours (176).

    • seniorityPercentagenumber | null

      Seniority allowance — a percentage of the base gross salary, applied directly.

    • seniorityobject | null

      Alternative to `seniorityPercentage` — computed from months of service as of a date, at a rate per completed year.

      Show child parametersHide child parameters
      • monthsinteger
      • asOfstring
      • ratePerYearnumber

        Default: 0.5

      • periodYearinteger | null
      • periodMonthinteger | null
    • overtimeHoursnumber | null
    • overtimeRatenumber | null

      Total % of the base hourly; defaults to 135 (base + 35% supplement).

    • nightHoursnumber | null
    • nightHoursRatenumber | null

      Defaults to 135.

    • holidayHoursnumber | null
    • holidayHoursRatenumber | null

      Defaults to 150.

    • sundayHoursnumber | null
    • sundayHoursRatenumber | null

      Defaults to 150.

    • holidayNightHoursnumber | null

      Combined holiday+night premium hours.

    • holidayNightRatenumber | null

      Defaults to 185.

    • sundayNightHoursnumber | null
    • sundayNightRatenumber | null

      Defaults to 185.

    • sundayHolidayHoursnumber | null
    • sundayHolidayRatenumber | null

      Defaults to 200.

    • sickFundHoursnumber | null

      Sick leave paid by ФZOM (the health fund) — hours only; the fund pays the employee directly.

    • sickFundRatenumber | null

      Defaults to 70 (% of the base hourly).

    • sickCompanyHoursnumber | null

      Sick leave paid by the employer.

    • sickCompanyRatenumber | null

      Defaults to 70.

    • shift1Hoursnumber | null
    • shift1Ratenumber | null

      Defaults to 100.

    • shift2Hoursnumber | null
    • shift2Ratenumber | null

      Defaults to 100.

    • shift3Hoursnumber | null
    • shift3Ratenumber | null

      Defaults to 135.

    • specialHoursnumber | null
    • specialRatenumber | null

      Defaults to 100.

    • unpaidHoursnumber | null
    • absentHoursnumber | null

      Hours outside the employment window in a partial month (unpaid, no compensation) — see also `period`/`placements`.

    • maternityHoursnumber | null

      Maternity-leave hours paid by ФЗОМ, carved out of the base at no employer cost.

    • fundHoursnumber | null

      The 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 | null

      When to top up contributions to the legal minimum base (50% of the average salary): only on partial months (default), always, or never.

      proratedalwaysoff
    • periodobject | null

      Alternative to `absentHours` — the target month; combine with `placements` to have Merot compute the proration for you.

      Show child parametersHide child parameters
      • yearinteger
      • monthinteger
    • placementsarray of object | null
      Show child parametersHide child parameters
      • startDatestring
      • endDatestring | null
    • hoursInformationalboolean

      Record the premium hours without changing gross/net — the contracted salary already covers them.

      Default: false

    • seniorityInformationalboolean

      Record the seniority percentage without adding it to gross/net.

      Default: false

    • bonusAmountstring | null

      A 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.

      grossnet

      Default: "net"

    • pitExemptboolean

      Default: false

    • noPersonalAllowanceboolean

      Drop the personal allowance (the employee claims it at a different, primary employer).

      Default: false

    • personalAllowancestring | null

      A decimal denar amount as a string, never a JSON number.

    • averageSalarystring | null

      A decimal denar amount as a string, never a JSON number.

    • deductionsarray of object | null

      Post-net deductions (insurance, union dues, court order, ...) — reduce netPaid only, never gross or contributions.

      Show child parametersHide child parameters
      • descriptionstringrequired
      • amountstringrequired

        A decimal denar amount as a string, never a JSON number.

    • rateOverridesobject | null

      Advanced — override individual statutory rate keys (see `GET /v1/rates`) for this calculation only.

    • effectiveDatestring | null

      Rates 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.

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

  • grossstring

    A decimal denar amount as a string, never a JSON number.

  • netstring

    A decimal denar amount as a string, never a JSON number.

  • contractorboolean

    Default: false

  • rateOverridesobject

    Advanced — override individual statutory rate keys (see `GET /v1/rates`) for this calculation only.

  • effectiveDatestring

    Rates in force on this date; defaults to now.

Returns

  • 200

    Both figures, denar-exact.

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());
Response
{
  "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

  • yearintegerrequired
  • monthintegerrequired
  • fundHoursnumberrequired

    The month's full fund of hours (e.g. 176).

  • placementsarray of objectrequired
    Show child parametersHide child parameters
    • startDatestringrequired
    • endDatestring | null

Returns

  • 200

    Proration result.

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.

Current MK statutory rates

#

GET /v1/rates

Returns

  • 200

    The rate set in force today.

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());
Response
{
  "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.

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());
Response
{
  "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

  • hireDatestringrequired
  • asOfDatestringrequired
  • employmentTypeenum
    full_timecontractor

    Default: "full_time"

  • partTimePercentnumber

    Default: 100

  • careerSeniorityYearsnumber

    Default: 0

  • under18boolean

    Default: false

  • effectiveDatestring | null

Returns

  • 200

    Accrual result.

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());
Response
{
  "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

  • yearintegerrequired
  • balanceRemainingAtYearEndnumberrequired
  • employerPreventedboolean

    True lifts the carry-over cap (the employer, not the employee, prevented the leave from being used).

    Default: false

  • effectiveDatestring

Returns

  • 200

    Carry-over result.

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());
Response
{
  "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

  • remainingDaysnumberrequired
  • last12MonthsGrossarray of stringrequired
  • terminationDatestringrequired
  • currencystringrequired
  • effectiveDatestring

Returns

  • 200

    Cash-out amount and line item.

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());

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

  • companyobjectrequired
    Show child parametersHide child parameters
    • taxIdstring | null
    • registrationNumberstring | null
    • namestringrequired
    • nameCyrstring | null
    • addressstring | null
    • addressStreetstring | null
    • addressNumberstring | null
    • addressAreastring | null
    • postalCodestring | null
    • faxstring | null
    • citystring | null
    • municipalityCodestring | null
    • managerNamestring | null
    • phonestring | null
    • emailstring | null
    • areaCodestring | null

      МПИН 2.3 — derived from `city` when absent.

    • fzomBranchstring | null

      МПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.

  • batchobjectrequired
    Show child parametersHide child parameters
    • periodMonthintegerrequired
    • periodYearintegerrequired
    • workingHoursnumber

      The month's fund of hours; falls back to 176 for any record that carries none.

      Default: 176

    • vidObvrskastring | null

      МПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.

    • vidObvrznikstring | null

      МПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).

    • filingVariantenum | null

      Advanced, 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 objectrequired
    Show child parametersHide child parameters
    • salaryGrossstringrequired

      A decimal denar amount as a string, never a JSON number.

    • salaryNetstringrequired

      A decimal denar amount as a string, never a JSON number.

    • pensionstringrequired

      A decimal denar amount as a string, never a JSON number.

    • healthstringrequired

      A decimal denar amount as a string, never a JSON number.

    • additionalHealthstringrequired

      A decimal denar amount as a string, never a JSON number.

    • employmentContribstringrequired

      A decimal denar amount as a string, never a JSON number.

    • pitstringrequired

      A decimal denar amount as a string, never a JSON number.

    • personalAllowancestring

      A decimal denar amount as a string, never a JSON number.

    • overtimeHoursnumber

      Default: 0

    • overtimePaystring

      A decimal denar amount as a string, never a JSON number.

    • nightHoursnumber

      Default: 0

    • nightPaystring

      A decimal denar amount as a string, never a JSON number.

    • holidayHoursnumber

      Default: 0

    • holidayPaystring

      A decimal denar amount as a string, never a JSON number.

    • sundayHoursnumber

      Default: 0

    • sundayPaystring

      A decimal denar amount as a string, never a JSON number.

    • workingHoursnumber | null

      Defaults to the batch's `workingHours`.

    • sickFundHoursnumber | null
    • sickFundRatenumber | null
    • sickFundPaystring

      A decimal denar amount as a string, never a JSON number.

    • sickCompanyHoursnumber | null
    • sickCompanyRatenumber | null
    • sickCompanyPaystring

      A decimal denar amount as a string, never a JSON number.

    • unpaidHoursnumber | null
    • absentHoursnumber | null

      МПИН 3.5 — hours outside the placement in a partial month.

    • serviceDaysnumber | null
    • pensionTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • healthTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • disabilityTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • employmentTopUpstring

      A 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 | null
    • sickFundBasestring

      A decimal denar amount as a string, never a JSON number.

    • maternityHoursnumber | null

      МПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).

    • maternityBasestring

      A decimal denar amount as a string, never a JSON number.

    • maternityPartialHoursnumber | null

      Hours worked after an early return from maternity leave (field 3.23 = 129).

    • clientTaxIdstring | null
    • netPaidstring

      A decimal denar amount as a string, never a JSON number.

    • employeeobjectrequired
      Show child parametersHide child parameters
      • embgstring | null

        ЕМБГ — the employee's unique master citizen number.

      • firstNamestringrequired
      • lastNamestringrequired
      • firstNameCyrstring | null
      • lastNameCyrstring | null
      • bankAccountstring | null
      • municipalityCodestring | null
      • fzomBranchstring | null
  • forceboolean

    Generate 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).

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());
Response

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

  • companyobjectrequired
    Show child parametersHide child parameters
    • taxIdstring | null
    • registrationNumberstring | null
    • namestringrequired
    • nameCyrstring | null
    • addressstring | null
    • addressStreetstring | null
    • addressNumberstring | null
    • addressAreastring | null
    • postalCodestring | null
    • faxstring | null
    • citystring | null
    • municipalityCodestring | null
    • managerNamestring | null
    • phonestring | null
    • emailstring | null
    • areaCodestring | null

      МПИН 2.3 — derived from `city` when absent.

    • fzomBranchstring | null

      МПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.

  • batchobjectrequired
    Show child parametersHide child parameters
    • periodMonthintegerrequired
    • periodYearintegerrequired
    • workingHoursnumber

      The month's fund of hours; falls back to 176 for any record that carries none.

      Default: 176

    • vidObvrskastring | null

      МПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.

    • vidObvrznikstring | null

      МПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).

    • filingVariantenum | null

      Advanced, 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 objectrequired
    Show child parametersHide child parameters
    • salaryGrossstringrequired

      A decimal denar amount as a string, never a JSON number.

    • salaryNetstringrequired

      A decimal denar amount as a string, never a JSON number.

    • pensionstringrequired

      A decimal denar amount as a string, never a JSON number.

    • healthstringrequired

      A decimal denar amount as a string, never a JSON number.

    • additionalHealthstringrequired

      A decimal denar amount as a string, never a JSON number.

    • employmentContribstringrequired

      A decimal denar amount as a string, never a JSON number.

    • pitstringrequired

      A decimal denar amount as a string, never a JSON number.

    • personalAllowancestring

      A decimal denar amount as a string, never a JSON number.

    • overtimeHoursnumber

      Default: 0

    • overtimePaystring

      A decimal denar amount as a string, never a JSON number.

    • nightHoursnumber

      Default: 0

    • nightPaystring

      A decimal denar amount as a string, never a JSON number.

    • holidayHoursnumber

      Default: 0

    • holidayPaystring

      A decimal denar amount as a string, never a JSON number.

    • sundayHoursnumber

      Default: 0

    • sundayPaystring

      A decimal denar amount as a string, never a JSON number.

    • workingHoursnumber | null

      Defaults to the batch's `workingHours`.

    • sickFundHoursnumber | null
    • sickFundRatenumber | null
    • sickFundPaystring

      A decimal denar amount as a string, never a JSON number.

    • sickCompanyHoursnumber | null
    • sickCompanyRatenumber | null
    • sickCompanyPaystring

      A decimal denar amount as a string, never a JSON number.

    • unpaidHoursnumber | null
    • absentHoursnumber | null

      МПИН 3.5 — hours outside the placement in a partial month.

    • serviceDaysnumber | null
    • pensionTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • healthTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • disabilityTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • employmentTopUpstring

      A 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 | null
    • sickFundBasestring

      A decimal denar amount as a string, never a JSON number.

    • maternityHoursnumber | null

      МПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).

    • maternityBasestring

      A decimal denar amount as a string, never a JSON number.

    • maternityPartialHoursnumber | null

      Hours worked after an early return from maternity leave (field 3.23 = 129).

    • clientTaxIdstring | null
    • netPaidstring

      A decimal denar amount as a string, never a JSON number.

    • employeeobjectrequired
      Show child parametersHide child parameters
      • embgstring | null

        ЕМБГ — the employee's unique master citizen number.

      • firstNamestringrequired
      • lastNamestringrequired
      • firstNameCyrstring | null
      • lastNameCyrstring | null
      • bankAccountstring | null
      • municipalityCodestring | null
      • fzomBranchstring | null
  • forceboolean

    Generate even if validation found blocking issues.

    Default: false

Returns

  • 200

    The МПИН TXT file (text/plain; charset=utf-16le).

  • 422

    mpin_validation_failed

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());
Response

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

  • companyobjectrequired
    Show child parametersHide child parameters
    • taxIdstring | null
    • registrationNumberstring | null
    • namestringrequired
    • nameCyrstring | null
    • addressstring | null
    • addressStreetstring | null
    • addressNumberstring | null
    • addressAreastring | null
    • postalCodestring | null
    • faxstring | null
    • citystring | null
    • municipalityCodestring | null
    • managerNamestring | null
    • phonestring | null
    • emailstring | null
    • areaCodestring | null

      МПИН 2.3 — derived from `city` when absent.

    • fzomBranchstring | null

      МПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.

  • batchobjectrequired
    Show child parametersHide child parameters
    • periodMonthintegerrequired
    • periodYearintegerrequired
    • workingHoursnumber

      The month's fund of hours; falls back to 176 for any record that carries none.

      Default: 176

    • vidObvrskastring | null

      МПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.

    • vidObvrznikstring | null

      МПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).

    • filingVariantenum | null

      Advanced, 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 objectrequired
    Show child parametersHide child parameters
    • recordsarray of object
      Show child parametersHide child parameters
      • salaryGrossstringrequired

        A decimal denar amount as a string, never a JSON number.

      • salaryNetstringrequired

        A decimal denar amount as a string, never a JSON number.

      • pensionstringrequired

        A decimal denar amount as a string, never a JSON number.

      • healthstringrequired

        A decimal denar amount as a string, never a JSON number.

      • additionalHealthstringrequired

        A decimal denar amount as a string, never a JSON number.

      • employmentContribstringrequired

        A decimal denar amount as a string, never a JSON number.

      • pitstringrequired

        A decimal denar amount as a string, never a JSON number.

      • personalAllowancestring

        A decimal denar amount as a string, never a JSON number.

      • overtimeHoursnumber

        Default: 0

      • overtimePaystring

        A decimal denar amount as a string, never a JSON number.

      • nightHoursnumber

        Default: 0

      • nightPaystring

        A decimal denar amount as a string, never a JSON number.

      • holidayHoursnumber

        Default: 0

      • holidayPaystring

        A decimal denar amount as a string, never a JSON number.

      • sundayHoursnumber

        Default: 0

      • sundayPaystring

        A decimal denar amount as a string, never a JSON number.

      • workingHoursnumber | null

        Defaults to the batch's `workingHours`.

      • sickFundHoursnumber | null
      • sickFundRatenumber | null
      • sickFundPaystring

        A decimal denar amount as a string, never a JSON number.

      • sickCompanyHoursnumber | null
      • sickCompanyRatenumber | null
      • sickCompanyPaystring

        A decimal denar amount as a string, never a JSON number.

      • unpaidHoursnumber | null
      • absentHoursnumber | null

        МПИН 3.5 — hours outside the placement in a partial month.

      • serviceDaysnumber | null
      • pensionTopUpstring

        A decimal denar amount as a string, never a JSON number.

      • healthTopUpstring

        A decimal denar amount as a string, never a JSON number.

      • disabilityTopUpstring

        A decimal denar amount as a string, never a JSON number.

      • employmentTopUpstring

        A 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 | null
      • sickFundBasestring

        A decimal denar amount as a string, never a JSON number.

      • maternityHoursnumber | null

        МПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).

      • maternityBasestring

        A decimal denar amount as a string, never a JSON number.

      • maternityPartialHoursnumber | null

        Hours worked after an early return from maternity leave (field 3.23 = 129).

      • clientTaxIdstring | null
      • netPaidstring

        A decimal denar amount as a string, never a JSON number.

      • employeeobjectrequired
        Show child parametersHide child parameters
        • embgstring | null

          ЕМБГ — the employee's unique master citizen number.

        • firstNamestringrequired
        • lastNamestringrequired
        • firstNameCyrstring | null
        • lastNameCyrstring | null
        • bankAccountstring | null
        • municipalityCodestring | null
        • fzomBranchstring | null

Returns

  • 200

    The combined МПИН TXT file.

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());
Response

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

  • companyobjectrequired
    Show child parametersHide child parameters
    • taxIdstring | null
    • registrationNumberstring | null
    • namestringrequired
    • nameCyrstring | null
    • addressstring | null
    • addressStreetstring | null
    • addressNumberstring | null
    • addressAreastring | null
    • postalCodestring | null
    • faxstring | null
    • citystring | null
    • municipalityCodestring | null
    • managerNamestring | null
    • phonestring | null
    • emailstring | null
    • areaCodestring | null

      МПИН 2.3 — derived from `city` when absent.

    • fzomBranchstring | null

      МПИН 3.4б — the ФЗО (health-fund) branch for the seat; derived from `city` when absent.

  • batchobjectrequired
    Show child parametersHide child parameters
    • periodMonthintegerrequired
    • periodYearintegerrequired
    • workingHoursnumber

      The month's fund of hours; falls back to 176 for any record that carries none.

      Default: 176

    • vidObvrskastring | null

      МПИН 1.3 — вид на обврска. Defaults to 101 (regular). 103 = correction.

    • vidObvrznikstring | null

      МПИН 1.4 — вид на обврзник. Defaults to 110 (legal entity).

    • filingVariantenum | null

      Advanced, 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 objectrequired
    Show child parametersHide child parameters
    • salaryGrossstringrequired

      A decimal denar amount as a string, never a JSON number.

    • salaryNetstringrequired

      A decimal denar amount as a string, never a JSON number.

    • pensionstringrequired

      A decimal denar amount as a string, never a JSON number.

    • healthstringrequired

      A decimal denar amount as a string, never a JSON number.

    • additionalHealthstringrequired

      A decimal denar amount as a string, never a JSON number.

    • employmentContribstringrequired

      A decimal denar amount as a string, never a JSON number.

    • pitstringrequired

      A decimal denar amount as a string, never a JSON number.

    • personalAllowancestring

      A decimal denar amount as a string, never a JSON number.

    • overtimeHoursnumber

      Default: 0

    • overtimePaystring

      A decimal denar amount as a string, never a JSON number.

    • nightHoursnumber

      Default: 0

    • nightPaystring

      A decimal denar amount as a string, never a JSON number.

    • holidayHoursnumber

      Default: 0

    • holidayPaystring

      A decimal denar amount as a string, never a JSON number.

    • sundayHoursnumber

      Default: 0

    • sundayPaystring

      A decimal denar amount as a string, never a JSON number.

    • workingHoursnumber | null

      Defaults to the batch's `workingHours`.

    • sickFundHoursnumber | null
    • sickFundRatenumber | null
    • sickFundPaystring

      A decimal denar amount as a string, never a JSON number.

    • sickCompanyHoursnumber | null
    • sickCompanyRatenumber | null
    • sickCompanyPaystring

      A decimal denar amount as a string, never a JSON number.

    • unpaidHoursnumber | null
    • absentHoursnumber | null

      МПИН 3.5 — hours outside the placement in a partial month.

    • serviceDaysnumber | null
    • pensionTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • healthTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • disabilityTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • employmentTopUpstring

      A 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 | null
    • sickFundBasestring

      A decimal denar amount as a string, never a JSON number.

    • maternityHoursnumber | null

      МПИН 3.24 — hours on ФЗОМ-borne maternity leave (field 3.23 = 128).

    • maternityBasestring

      A decimal denar amount as a string, never a JSON number.

    • maternityPartialHoursnumber | null

      Hours worked after an early return from maternity leave (field 3.23 = 129).

    • clientTaxIdstring | null
    • netPaidstring

      A decimal denar amount as a string, never a JSON number.

    • employeeobjectrequired
      Show child parametersHide child parameters
      • embgstring | null

        ЕМБГ — the employee's unique master citizen number.

      • firstNamestringrequired
      • lastNamestringrequired
      • firstNameCyrstring | null
      • lastNameCyrstring | null
      • bankAccountstring | null
      • municipalityCodestring | null
      • fzomBranchstring | null
  • forceboolean

    Generate even if validation found blocking issues.

    Default: false

Returns

  • 200

    Validation result.

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());
Response
{
  "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

  • companyobjectrequired
    Show child parametersHide child parameters
    • taxIdstring | null
    • namestringrequired
    • emailstring | null
    • phonestring | null
    • contactPersonstring | null
    • municipalityCodestring | null
  • monthintegerrequired
  • yearintegerrequired
  • recordsarray of objectrequired
    Show child parametersHide child parameters
    • salaryGrossstringrequired

      A decimal denar amount as a string, never a JSON number.

    • employeeobjectrequired
      Show child parametersHide child parameters
      • embgstring | null
      • firstNamestringrequired
      • lastNamestringrequired
      • bankAccountstring | null

Returns

  • 200

    The ПДД-ГИ XML file.

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());
Response

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

  • issuerobjectrequired
    Show child parametersHide child parameters
    • edbstringrequired
    • namestringrequired
    • bankAccountstring | null
    • emailstring | null
    • phonestring | null
  • periodFromstringrequired
  • periodTostringrequired
  • commentstring | null
  • rowsarray of objectrequired
    Show child parametersHide child parameters
    • embgstringrequired
    • firstNamestringrequired
    • lastNamestringrequired
    • municipalityCodestring | null
    • subtypeIncomestring | null
    • grossIncomestringrequired

      A decimal denar amount as a string, never a JSON number.

    • bankAccountstring | null

Returns

  • 200

    The е-ППД XML file.

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());
Response

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

  • netstringrequired

    A decimal denar amount as a string, never a JSON number.

Returns

  • 200

    Gross amount.

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());
Response
{
  "net": "20000",
  "gross": "22222"
}

Payslips

Payslip PDF generation in MK, SQ and EN.

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

  • companyobjectrequired
    Show child parametersHide child parameters
    • namestringrequired
    • taxIdstring | null
    • addressstring | null
  • employeeobjectrequired
    Show child parametersHide child parameters
    • firstNamestringrequired
    • lastNamestringrequired
    • embgstring | null
    • positionstring | null
  • periodobjectrequired
    Show child parametersHide child parameters
    • yearintegerrequired
    • monthintegerrequired
  • calculationobjectrequired
    Show child parametersHide child parameters
    • salaryGrossstring

      A decimal denar amount as a string, never a JSON number.

    • baseGrossstring

      A decimal denar amount as a string, never a JSON number.

    • pensionstring

      A decimal denar amount as a string, never a JSON number.

    • healthstring

      A decimal denar amount as a string, never a JSON number.

    • employmentContribstring

      A decimal denar amount as a string, never a JSON number.

    • additionalHealthstring

      A decimal denar amount as a string, never a JSON number.

    • totalContributionsstring

      A decimal denar amount as a string, never a JSON number.

    • pensionTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • healthTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • disabilityTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • employmentTopUpstring

      A decimal denar amount as a string, never a JSON number.

    • personalAllowancestring

      A decimal denar amount as a string, never a JSON number.

    • taxableIncomestring

      A decimal denar amount as a string, never a JSON number.

    • pitstring

      A decimal denar amount as a string, never a JSON number.

    • salaryNetstring

      A decimal denar amount as a string, never a JSON number.

    • deductionsTotalstring

      A decimal denar amount as a string, never a JSON number.

    • netPaidstring

      A decimal denar amount as a string, never a JSON number.

    • workingHoursnumber
    • seniorityPercentagenumber
    • senioritystring

      A decimal denar amount as a string, never a JSON number.

    • bonusAmountstring

      A decimal denar amount as a string, never a JSON number.

    • bonusBasisenum
      grossnet
    • overtimeHoursnumber
    • overtimePaystring

      A decimal denar amount as a string, never a JSON number.

    • nightHoursnumber
    • nightPaystring

      A decimal denar amount as a string, never a JSON number.

    • holidayHoursnumber
    • holidayPaystring

      A decimal denar amount as a string, never a JSON number.

    • sundayHoursnumber
    • sundayPaystring

      A decimal denar amount as a string, never a JSON number.

    • sickHoursnumber
    • sickRatenumber
    • sickPaystring

      A decimal denar amount as a string, never a JSON number.

    • sickFundHoursnumber
    • sickFundRatenumber
    • sickFundPaystring

      A decimal denar amount as a string, never a JSON number.

    • sickCompanyHoursnumber
    • sickCompanyRatenumber
    • sickCompanyPaystring

      A decimal denar amount as a string, never a JSON number.

    • shift1Hoursnumber
    • shift1Ratenumber
    • shift1Paystring

      A decimal denar amount as a string, never a JSON number.

    • shift2Hoursnumber
    • shift2Ratenumber
    • shift2Paystring

      A decimal denar amount as a string, never a JSON number.

    • shift3Hoursnumber
    • shift3Ratenumber
    • shift3Paystring

      A decimal denar amount as a string, never a JSON number.

    • specialHoursnumber
    • specialRatenumber
    • specialPaystring

      A decimal denar amount as a string, never a JSON number.

    • unpaidHoursnumber
    • absentHoursnumber
    • maternityHoursnumber
    • holidayNightHoursnumber
    • holidayNightRatenumber
    • holidayNightPaystring

      A decimal denar amount as a string, never a JSON number.

    • sundayNightHoursnumber
    • sundayNightRatenumber
    • sundayNightPaystring

      A decimal denar amount as a string, never a JSON number.

    • sundayHolidayHoursnumber
    • sundayHolidayRatenumber
    • sundayHolidayPaystring

      A decimal denar amount as a string, never a JSON number.

    • prorationobject | null

      Present only when the request used `period` + `placements`.

      Show child parametersHide child parameters
      • serviceDaysnumber | null
      • absentHoursnumber
      • paidHoursnumber | null
      • hireDayinteger | null
      • leaveDayinteger | null
  • languageenumrequired
    mksqen

    Default: "mk"

Returns

  • 200

    The payslip PDF.

  • 422

    validation_failed — one or more fields failed validation.

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());
Response

application/pdf — binary/file response, no JSON body to preview.

Health

Public status endpoint (unauthenticated).

Service status

#

GET /v1/health

Returns

  • 200

    Current status.

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());
Response
{
  "status": "ok",
  "version": "2026-09-26"
}