Skip to content

Tax authority codes

When the document reaches the tax authority (UJP), their answer comes back with their own codes. Those never appear in our error catalogue — it carries only the EARH-xxxx codes, which are ours.

Both statements are true, and together they mean a code from the tax authority has nowhere to be looked up in the catalogue. This page is that place.

The detail of an outbound document carries a ujp block:

FieldWhat it isWhen it is filled
euidthe identifier the tax authority assigned to the invoice on acceptanceas soon as they accept it
qrLinkthe QR code link exactly as they returned it — print it as it isas soon as they accept it
statethe state reported by the tax authority, in their vocabularywhen the state is read back from them
codethe code they return when the answer is a refusal; null when there is noneon a refusal, immediately
messagethe text they return, in their own language. It is not stable — do not match on iton a refusal, immediately
checkedAtwhen we last read that answerwhen the state is read back from them

The block is null while the document has not been forwarded to the tax authority yet.

⛔ An accepted invoice carries only euid and qrLink; the other four are null. On acceptance the tax authority returns an identifier and a link, not a state — state and checkedAt are filled later, when the state is read back. What says the invoice was accepted is euid, not state.

The tax authority’s codes in an outbound document are taken care of by eArhiva.mk, through its built-in mapping. You send the codes used in your own system; for every company using our system, eArhiva.mk maps them onto the tax authority’s code lists according to that company’s own codes.

On submission we do not check whether the codes inside the document are known. POST outbound only checks that the document is structurally sound — valid UBL, one document, a well-formed tax number. The codes are resolved later, when processing maps the document for the tax authority.

⚠️ So 201 does not mean the codes are fine. If one of them has no mapping for that organisation, it shows up later — in the document’s status and failureCode, not as a rejection of the submission itself.

Mapping UBL to the tax authority’s fields

Section titled “Mapping UBL to the tax authority’s fields”

This is the whole mapping: every path the converter actually reads, and the field at the tax authority it ends up in. Anything not in the table is not read — no matter whether it is valid UBL or carries a value.

UBL pathField at the tax authority
cbc:IDheader.docNumber
cbc:IssueDateheader.docDate
cbc:DueDatedocPayment.docPaymentTypeDueDate
cbc:DocumentCurrencyCodedocPayment.docCurrencyCode
cbc:Noteheader.docNotes
cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Nameseller.sellerName
cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyIDseller.sellerTin
…SupplierParty/…/cac:PostalAddress/cbc:StreetNameseller.sellerAddress.streetAddress
…SupplierParty/…/cac:PostalAddress/cbc:CityNameseller.sellerAddress.city
…SupplierParty/…/cac:PostalAddress/cbc:PostalZoneseller.sellerAddress.postalCode
…SupplierParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCodeseller.sellerCCode
cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Namebuyer.buyerName
cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyIDbuyer.buyerTin
…CustomerParty/…/cac:PostalAddress/cbc:StreetNamebuyer.buyerAddress.streetAddress
…CustomerParty/…/cac:PostalAddress/cbc:CityNamebuyer.buyerAddress.city
…CustomerParty/…/cac:PostalAddress/cbc:PostalZonebuyer.buyerAddress.postalCode
…CustomerParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCodebuyer.buyerCCode
cac:LegalMonetaryTotal/cbc:TaxExclusiveAmountdocTotals.docNetAmount
cac:LegalMonetaryTotal/cbc:TaxInclusiveAmountdocTotals.docGrossAmount
cac:LegalMonetaryTotal/cbc:PayableAmountdocTotals.docFinalAmount
cac:TaxTotal/cbc:TaxAmountdocTotals.docVatAmount
cac:InvoiceLine/cac:Item/cbc:NamedocItems[].docItemDesc
cac:InvoiceLine/cbc:InvoicedQuantitydocItems[].docItemQty
cac:InvoiceLine/cbc:InvoicedQuantity/@unitCodedocItems[].docItemMUnit
cac:InvoiceLine/cac:Price/cbc:PriceAmountdocItems[].docItemUnitPriceWoVat
cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:PercentdocItems[].docItemVat

Three more paths are read, but they do not end up in a UJP field — they come in as your own ERP codes and go from there through the organization’s mapping to the UJP code list:

UBL pathERP code
cbc:InvoiceTypeCode · on a CreditNote: cbc:CreditNoteTypeCodethe document type
…SupplierParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCodethe seller’s country
…CustomerParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCodethe buyer’s country

⛔ So 380 is not the code that reaches UJP: 380 is a UBL standard code, 100 is the UJP code (“Invoice”), and the path between them goes through your organization’s mapping — the place where one UJP type may have one or several of your own codes. For the two countries a mapping is normally not needed: the country code list carries two-letter ISO codes, so MK passes as it is. Which of your codes are already mapped to a given entry is visible in erpCodes — see below.

Which of these are required is judged by the pre-submission check; it returns status 250 with EARH-4001, and the individual reasons come in validationErrors with codes EARH-5001…EARH-5012. The ones most often missing — the recipient’s address and the rate per line — are listed on the Formats page.

The code lists of the tax authority are read through the integration API, for your environment and your organisation:

Code listWhat it carries
document-statusesthe states of a document at the tax authority — what appears in ujp.state
reject-reasonsthe rejection reasons — the source of the code you send in POST decision, and of the reason that comes back when a document is rejected
void-reasonsthe cancellation reasons

That is the authoritative source, not this page. The lists change at the tax authority and are read per environment; what is here is a dated snapshot. When the two differ, the answer of GET /codelists/{type} is the correct one.

A new entry in a code list is not a breaking change — no code is ever removed and none changes its meaning.

Snapshot of the document-statuses code list, 29.08.2026. The name and the meaning are given in English. The tax authority publishes this list only in Macedonian, so the English wording is ours, not official.

CodeNameMeaning
00Drafte-invoices that have been created, but are not final and have not been sent to the recipient
01Sent (New)an e-invoice issued by the issuer to the recipient, signed, validated and recorded in the system
03Acceptedan e-invoice issued by the issuer to the recipient and accepted by the recipient
04Automatically acceptedaccepted automatically when the deadline expired
05Rejectedan e-invoice issued by the issuer, received by the recipient and rejected with a stated explanation or reason
06Automatically rejectedrejected automatically when the recipient neither accepts nor rejects it within the statutory deadline
07Reversedan e-invoice received or rejected by the recipient, but reversed by the issuer (with prior confirmation from the recipient)
09Correctedcorrected
10Recordedrecorded in the system
11Withdrawnan e-invoice issued to the recipient, but withdrawn by the seller before it was accepted or rejected

There is deliberately no table here. The reject-reasons and void-reasons lists are read per environment and change at the tax authority; copied here, they would go stale silently — and a wrong code on a rejection is refused with EARH-2022, which costs more than one extra call.

Read them live:

  • GET /codelists/reject-reasons
  • GET /codelists/void-reasons

The answer carries code and name for every active entry — the very values we use internally.