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.
Where they appear
Section titled “Where they appear”The detail of an outbound document carries a ujp block:
| Field | What it is | When it is filled |
|---|---|---|
euid | the identifier the tax authority assigned to the invoice on acceptance | as soon as they accept it |
qrLink | the QR code link exactly as they returned it — print it as it is | as soon as they accept it |
state | the state reported by the tax authority, in their vocabulary | when the state is read back from them |
code | the code they return when the answer is a refusal; null when there is none | on a refusal, immediately |
message | the text they return, in their own language. It is not stable — do not match on it | on a refusal, immediately |
checkedAt | when we last read that answer | when 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.
eArhiva.mk does the mapping
Section titled “eArhiva.mk does the mapping”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 path | Field at the tax authority |
|---|---|
cbc:ID | header.docNumber |
cbc:IssueDate | header.docDate |
cbc:DueDate | docPayment.docPaymentTypeDueDate |
cbc:DocumentCurrencyCode | docPayment.docCurrencyCode |
cbc:Note | header.docNotes |
cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name | seller.sellerName |
cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID | seller.sellerTin |
…SupplierParty/…/cac:PostalAddress/cbc:StreetName | seller.sellerAddress.streetAddress |
…SupplierParty/…/cac:PostalAddress/cbc:CityName | seller.sellerAddress.city |
…SupplierParty/…/cac:PostalAddress/cbc:PostalZone | seller.sellerAddress.postalCode |
…SupplierParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCode | seller.sellerCCode |
cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name | buyer.buyerName |
cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID | buyer.buyerTin |
…CustomerParty/…/cac:PostalAddress/cbc:StreetName | buyer.buyerAddress.streetAddress |
…CustomerParty/…/cac:PostalAddress/cbc:CityName | buyer.buyerAddress.city |
…CustomerParty/…/cac:PostalAddress/cbc:PostalZone | buyer.buyerAddress.postalCode |
…CustomerParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCode | buyer.buyerCCode |
cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount | docTotals.docNetAmount |
cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount | docTotals.docGrossAmount |
cac:LegalMonetaryTotal/cbc:PayableAmount | docTotals.docFinalAmount |
cac:TaxTotal/cbc:TaxAmount | docTotals.docVatAmount |
cac:InvoiceLine/cac:Item/cbc:Name | docItems[].docItemDesc |
cac:InvoiceLine/cbc:InvoicedQuantity | docItems[].docItemQty |
cac:InvoiceLine/cbc:InvoicedQuantity/@unitCode | docItems[].docItemMUnit |
cac:InvoiceLine/cac:Price/cbc:PriceAmount | docItems[].docItemUnitPriceWoVat |
cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent | docItems[].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 path | ERP code |
|---|---|
cbc:InvoiceTypeCode · on a CreditNote: cbc:CreditNoteTypeCode | the document type |
…SupplierParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCode | the seller’s country |
…CustomerParty/…/cac:PostalAddress/cac:Country/cbc:IdentificationCode | the 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 live source — GET /codelists/{type}
Section titled “The live source — GET /codelists/{type}”The code lists of the tax authority are read through the integration API, for your environment and your organisation:
| Code list | What it carries |
|---|---|
document-statuses | the states of a document at the tax authority — what appears in ujp.state |
reject-reasons | the 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-reasons | the 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.
Document states at the tax authority
Section titled “Document states at the tax authority”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.
| Code | Name | Meaning |
|---|---|---|
00 | Draft | e-invoices that have been created, but are not final and have not been sent to the recipient |
01 | Sent (New) | an e-invoice issued by the issuer to the recipient, signed, validated and recorded in the system |
03 | Accepted | an e-invoice issued by the issuer to the recipient and accepted by the recipient |
04 | Automatically accepted | accepted automatically when the deadline expired |
05 | Rejected | an e-invoice issued by the issuer, received by the recipient and rejected with a stated explanation or reason |
06 | Automatically rejected | rejected automatically when the recipient neither accepts nor rejects it within the statutory deadline |
07 | Reversed | an e-invoice received or rejected by the recipient, but reversed by the issuer (with prior confirmation from the recipient) |
09 | Corrected | corrected |
10 | Recorded | recorded in the system |
11 | Withdrawn | an e-invoice issued to the recipient, but withdrawn by the seller before it was accepted or rejected |
Rejection and cancellation reasons
Section titled “Rejection and cancellation reasons”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-reasonsGET /codelists/void-reasons
The answer carries code and name for every active entry — the very values we use internally.