Оди на содржината

Процес: излезни е-фактури

Оваа страница те води од нула до една излезна е-фактура што УЈП ја примил и прифатил. Не треба да знаеш ништо однапред: секој чекор кажува што правиш, ти дава готова команда, покажува каков одговор да очекуваш и што да направиш ако добиеш нешто друго.

Сè се случува на sandbox серверот (https://sandbox.api.earhiva.mk). Тој ја праќа фактурата на тест системот на УЈП, па ниту една фактура од оваа страница не е вистинска и не создава даночна обврска.

Пред чекорите, целата слика. Фактурата минува низ четири места:

#КадеШто се случува
1Твојот софтверја составува фактурата како UBL 2.1 XML и ја праќа со еден повик
2eArhiva.mkја заведува, проверува, ја претвора во порака за УЈП и ја потпишува
3УЈПја прима, ја проверува и ја прифаќа или одбива
4Примачотја гледа кај себе и може да ја прифати или одбие

Ти праќаш само еднаш (чекор 4 подолу), а потоа само прашуваш докаде стигнала (чекорите 5 и 6). Потпишувањето и праќањето кон УЈП ги правиме ние.

ШтоЗошто
е-поштаза барањето за пристап
команда curlза повиците; ја има на Windows 10 и 11, macOS и Linux
една празна папкатаму ќе ја снимиш фактурата и од таму ќе ги извршуваш командите
податоци за клиентотод чие име ќе праќаш — име, ЕДБ и e-УЈП ID (в. чекор 1)

  1. Пристапот се бара од нас по е-пошта, на support@earhiva.mk, со наслов Барање за пристап кон sandbox серверот. Копирај го текстот подолу и пополни ги сите три дела:

    Текст на барањето · копирај го и пополни го
    Барање за пристап кон sandbox серверот на eArhiva.mk
    1. Податоци за интеграторот
    име на компанијата:
    лице за контакт:
    е-пошта:
    телефон:
    име на апликацијата што ќе го користи API:
    2. Податоци за клиентот (од чие име ќе се праќаат фактурите)
    име на компанијата:
    ЕДБ:
    e-УЈП ID:
    лице за контакт:
    е-пошта:
    3. Примач на пробните фактури
    име на компанијата:
    ЕДБ:

    Трите дела се потребни затоа што фактурата стигнува до УЈП само ако три работи се подготвени однапред:

    ШтоКој го даваШто се случува без него
    ЕДБ на клиентотклиентотфактурата не заминува кон УЈП
    e-УЈП ID на клиентотго доделува УЈП кога клиентот се регистрира за е-фактури; клиентот го имафактурата не заминува кон УЈП
    Примачот заведен како партнер на клиентот⚠️ веќе не е предуслов — примач што го нема се заведува од самата фактура; делот 3 сепак помага да го провериме приемот пред тебе—

    Што ќе добиеш назад: клуч (долга низа знаци) и потврда дека организацијата на клиентот е подготвена за праќање кон УЈП. Клучот чувај го како лозинка — со него се праќа во име на клиентот.

  2. Ова е најкраткиот повик: не праќа ништо, само кажува дали клучот е примен. Во командата замени ги двете вредности во < >:

    • <ВАШИОТ КЛУЧ> — клучот од чекорот 1
    • <ИМЕ И ВЕРЗИЈА НА ВАШАТА АПЛИКАЦИЈА> — слободен текст, на пример SmetkovodstvoPro/2.4
    Барање · ова го извршуваш ти
    curl -i "https://sandbox.api.earhiva.mk/api/integration/v1/ping" \
    -H "X-Api-Key: <ВАШИОТ КЛУЧ>" \
    -H "X-Software-Id: <ИМЕ И ВЕРЗИЈА НА ВАШАТА АПЛИКАЦИЈА>"

    Ако е во ред, првиот ред од одговорот е HTTP/1.1 200 OK, а на крајот стои:

    Одговор од API-то · вредностите се пример
    {
    "ok": true,
    "keyValid": true,
    "serverTime": "2026-09-11T10:14:03.412+02:00"
    }

    Ако не е:

    ДобивашШто значиШто правиш
    401клучот не е препишан точно, или е истеченкопирај го повторно, без празни места на почетокот и крајот
    403клучот не носи право за интеграцискиот API, или адресата од која праќаш не е дозволенапиши на поддршката и прати го целиот одговор
    400 со EARH-1008го нема заглавието X-Software-Idдодај го — тоа е задолжително во секој повик
  3. Преземи ја готовата фактура invoice-ubl21.xml и сними ја во празната папка под името invoice.xml.

    Отвори ја во кој било уредувач на текст и смени точно четири работи. Сè друго остави го како што е.

    #ШтоКаде во фајлотСо што
    1број на фактурата<cbc:ID>0001/2026</cbc:ID>нов број, на пример TEST-0001
    2датум на издавање<cbc:IssueDate>…</cbc:IssueDate>денешниот датум, во облик 2026-09-11
    3испраќачcac:AccountingSupplierParty — cbc:Name и cbc:CompanyIDимето и ЕДБ-то на клиентот од делот 2 од барањето
    4примачcac:AccountingCustomerParty — cbc:Name, cbc:CompanyID и адресата во cac:PostalAddressимето, ЕДБ-то и адресата на примачот од делот 3 од барањето

    По измената, тие делови изгледаат вака:

    invoice.xml · само сменетите делови
    <cbc:ID>TEST-0001</cbc:ID>
    <cbc:IssueDate>2026-09-11</cbc:IssueDate>
    <cac:AccountingSupplierParty>
    <cac:Party>
    <cac:PartyName>
    <cbc:Name><ИМЕ НА КЛИЕНТОТ></cbc:Name>
    </cac:PartyName>
    <cac:PartyTaxScheme>
    <cbc:CompanyID><ЕДБ НА КЛИЕНТОТ></cbc:CompanyID>
    </cac:PartyTaxScheme>
    </cac:Party>
    </cac:AccountingSupplierParty>
    <cac:AccountingCustomerParty>
    <cac:Party>
    <cac:PartyName>
    <cbc:Name><ИМЕ НА ПРИМАЧОТ></cbc:Name>
    </cac:PartyName>
    <cac:PostalAddress>
    <cbc:StreetName><УЛИЦА И БРОЈ></cbc:StreetName>
    <cbc:CityName><ГРАД></cbc:CityName>
    <cbc:PostalZone><ПОШТЕНСКИ БРОЈ></cbc:PostalZone>
    <cac:Country>
    <cbc:IdentificationCode>MK</cbc:IdentificationCode>
    </cac:Country>
    </cac:PostalAddress>
    <cac:PartyTaxScheme>
    <cbc:CompanyID><ЕДБ НА ПРИМАЧОТ></cbc:CompanyID>
    </cac:PartyTaxScheme>
    </cac:Party>
    </cac:AccountingCustomerParty>

    ⛔ При секој нов обид земи нов број на фактура и нова X-Idempotency-Key. Истата ознака со иста содржина го враќа првиот одговор и не создава втор документ (в. чекор 4). Но иста фактура пратена со нова ознака создава втор документ кај нас и заминува кај УЈП по втор пат — а дали таму ќе биде прифатена или одбиена како дупликат зависи од нивниот регистар и не е под наша контрола. Затоа: TEST-0002, TEST-0003 и така натаму.

    ⛔ Не ги бриши адресата на примачот и стапката на ДДВ на ставката (cac:ClassifiedTaxCategory). Приемот не ги бара, но УЈП ги бара: без нив фактурата застанува пред УЈП со EARH-4001.

  4. Изврши ја командата од папката каде е invoice.xml. Покрај двете познати заглавија, ова барање носи уште две:

    • X-Idempotency-Key — твоја ознака за ова праќање. Земи ја од бројот на фактурата, на пример invoice-TEST-0001. Ако врската се прекине и ја повториш истата команда, нема да се создаде втора фактура — ќе го добиеш одговорот од првиот пат.

      ⛔ Ознаката е врзана за содржината, не само за себе. Ист клуч со иста содржина е повторување и враќа 200; ист клуч со изменета содржина се одбива со EARH-2010. Значи ако го прегенерираш документот по поправка, тој бара нова ознака. Заради тоа ознака изведена само од идентитетот на фактурата (фирма + година + тип + број) удира во 409 при секој втор обид — додај ѝ и нешто што се менува со содржината (верзија, време на генерирање, збир од телото).

    • Prefer: wait=30 — одговорот да почека до 30 секунди додека нашата обработка не ја поднесе фактурата кон УЈП, и да ти го врати исходот, заедно со бројот што УЈП ѝ го доделил. Без ова заглавие одговорот доаѓа веднаш, само со потврда дека фактурата е примена.

      ⚠️ wait е горна граница на чекањето, не гаранција за конечен исход. Ако обработката трае подолго, одговорот доаѓа по истекот со состојбата што важи во тој момент — најчесто 210. Тоа не е грешка и не значи дека нешто паднало: состојбата се дочитува со GET outbound/{docUid}. Заглавието Preference-Applied кажува колку навистина е чекано.

    Барање · ова го извршуваш ти
    curl -i -X POST "https://sandbox.api.earhiva.mk/api/integration/v1/outbound" \
    -H "X-Api-Key: <ВАШИОТ КЛУЧ>" \
    -H "X-Software-Id: <ИМЕ И ВЕРЗИЈА НА ВАШАТА АПЛИКАЦИЈА>" \
    -H "X-Idempotency-Key: invoice-TEST-0001" \
    -H "Prefer: wait=30" \
    -H "Content-Type: application/xml" \
    --data-binary @invoice.xml

    Ако е во ред, првиот ред е HTTP/1.1 201 Created, меѓу заглавијата стои Preference-Applied: wait=30, а на крајот:

    Одговор од API-то · вредностите се пример, твоите ќе бидат други
    {
    "docUid": "eah_o_01K3F9YB2M7Q4XVZ8NDHRPTC5E",
    "status": 270,
    "receivedAt": "2026-09-11T10:16:41.088+02:00",
    "alreadyExisted": false,
    "failureCode": null,
    "ujp": {
    "euid": "<УЈП ID>", // бројот што УЈП ѝ го доделил на фактурата
    "qrLink": "https://…/euid/<УЈП ID>", // врската за QR кодот, онака како ја вратил УЈП
    "state": null,
    "code": null,
    "message": null,
    "checkedAt": null
    }
    }

    📌 euid и qrLink се пополнети, останатите три се празни — тоа е точниот изглед. Кога УЈП ја прифаќа фактурата, тој враќа идентификатор и врска, не и состојба. Полињата state, code, message и checkedAt се пополнуваат подоцна, кога состојбата ќе се прочита од УЈП — прашај со GET outbound/{docUid} кога ти треба. ⛔ Не чекај на нив за да заклучиш дека фактурата е примена: тоа го кажува euid.

    📌 Копирај ја вредноста на docUid и чувај ја. Тоа е бројот на фактурата кај нас, и секоја следна команда го бара. Вредноста погоре е само пример.

    Каде оди понатаму зависи од одговорот:

    Во одговоротШто правиш
    ujp не е nullисходот од УЈП е веќе тука — ✅ оди директно на чекорот 6
    status е 210 или 220, а "ujp": null30-те секунди поминале пред исходот — тоа не е грешка, обработката продолжува. Оди на чекорот 5
    status е 225 или 250фактурата застанала пред УЈП или УЈП ја одбил — оди на чекорот 6, таму стои што да направиш

    Ако не е:

    ДобивашШто значиШто правиш
    400 со шифра од EARH-2001 до EARH-2007фајлот не е во ред — лош XML, недостасува задолжително поле, или издавачот не е твојата организација (EARH-2007)шифрата кажува кое; види го каталогот на грешки, поправи и прати повторно
    400 со EARH-2011телото носи повеќе од една фактураедно барање носи точно еден документ; подели ги
    400 со EARH-2013го нема заглавието X-Idempotency-Keyдодај го — без него не можеме да разликуваме повторено барање од нова фактура
    409 со EARH-2010истата X-Idempotency-Key веќе е употребена за друга содржинасмени го бројот на фактурата и ознаката — на пример TEST-0002 и invoice-TEST-0002
    413 со EARH-2012фактурата е преголемаодговорот ја именува дозволената големина за таа организација; отстрани го прилогот што ја крева
    200 наместо 201, со "alreadyExisted": trueоваа фактура е веќе пратена со истата ознакане е грешка — docUid е истиот од првиот пат
  5. Овој чекор ти треба само ако одговорот од чекорот 4 немал исход од УЈП. Обработката продолжува во позадина — фактурата се проверува, потпишува и праќа кон УЈП. Со командата подолу прашуваш докаде стигнала, со eArhiva ID-то. Замени го <docUid> со вредноста од чекорот 4:

    Барање · ова го извршуваш ти
    curl -i "https://sandbox.api.earhiva.mk/api/integration/v1/outbound/<docUid>" \
    -H "X-Api-Key: <ВАШИОТ КЛУЧ>" \
    -H "X-Software-Id: <ИМЕ И ВЕРЗИЈА НА ВАШАТА АПЛИКАЦИЈА>"

    Гледај го полето status во одговорот. Повторувај ја командата на секои 10 секунди додека статусот не стане еден од последните два реда во табелата:

    statusШто значиШто правиш
    210примена кај нас, обработката уште не почналачекај и прашај повторно
    220се проверува и потпишувачекај и прашај повторно
    230поднесена кај УЈП✅ оди на чекорот 6
    270архивирана — по поднесувањето фактурата оди во архивата сама✅ оди на чекорот 6
    225 или 250застанала пред УЈП, или УЈП ја одбилоди на чекорот 6 — таму стои што да направиш

    Архивирањето следува веднаш по поднесувањето, па можно е 230 воопшто да не го видиш и од 220 директно да добиеш 270. Тоа не е проблем.

    Ако статусот стои на 210 или 220 подолго од 10 минути, пиши на поддршката и прати го docUid.

  6. Во последниот одговор — од чекорот 4 или од чекорот 5 — провери три полиња. Фактурата е прифатена кај УЈП кога сите три се исполнети:

    ПолеМора да биде
    status230, 240 или 270
    failureCodenull
    ujpне е null, внатре во него code е null, а euid го носи УЈП ID-то

    Вака изгледа успешен одговор:

    Одговор од API-то · фактура прифатена кај УЈП
    {
    "docUid": "eah_o_01K3F9YB2M7Q4XVZ8NDHRPTC5E",
    "status": 270,
    "failureCode": null, // ништо не паднало
    "failureMessage": null,
    "createdAt": "2026-09-11T10:16:41.088+02:00",
    "partnerEdb": "<ЕДБ НА ПРИМАЧОТ>",
    "documentNumber": "TEST-0001",
    "amount": 1180.00,
    "currency": "MKD",
    "validationErrors": [],
    "ujp": { // не е null — фактурата била кај УЈП
    "euid": "<УЈП ID>", // бројот што УЈП ѝ го доделил на фактурата
    "qrLink": "https://…/euid/<УЈП ID>", // врската за QR кодот, онака како ја вратил УЈП
    "state": null, // се пополнува подоцна, при читање на состојбата
    "code": null, // УЈП немал забелешка
    "message": null,
    "checkedAt": null
    }
    }

    Ако фактурата ја печатиш во свој PDF, QR кодот на неа е ujp.qrLink — втисни ја таа вредност таква каква што е. ⛔ Не ја составувај од euid: адресата е различна на тест и во продукција, и ја одредува УЈП. Со Prefer: wait од чекорот 4 и euid и qrLink стигнуваат во истиот одговор.

    Целиот пат на фактурата, со време за секоја промена, го гледаш со историјата:

    Барање · ова го извршуваш ти
    curl -i "https://sandbox.api.earhiva.mk/api/integration/v1/outbound/<docUid>/history" \
    -H "X-Api-Key: <ВАШИОТ КЛУЧ>" \
    -H "X-Software-Id: <ИМЕ И ВЕРЗИЈА НА ВАШАТА АПЛИКАЦИЈА>"
    Одговор од API-то · фактура што поминала до крај
    {
    "docUid": "eah_o_01K3F9YB2M7Q4XVZ8NDHRPTC5E",
    "transitions": [
    { "status": 210, "at": "2026-09-11T10:16:41", "failureCode": null },
    { "status": 220, "at": "2026-09-11T10:16:49", "failureCode": null },
    { "status": 230, "at": "2026-09-11T10:17:03", "failureCode": null },
    { "status": 270, "at": "2026-09-11T10:17:05", "failureCode": null }
    ]
    }

    Ако не е прифатена, најди го својот случај:

    ГледашШто се случилоШто правиш
    status е 225, или 270 со "ujp": nullфактурата е проверена, но не е пратена кон УЈП — на организацијата на клиентот ѝ недостасува ЕДБ или e-УЈП IDпиши на поддршката и прати го docUid; кога ќе ти потврдат, прати нова фактура со нов број
    250 со EARH-4005примачот не можел ниту да се разреши ниту да се заведе — редок случај, зашто непознат примач се заведува од самиот документvalidationErrors го носи EARH-5020 со истата причина; провери го ЕДБ-то на примачот во invoice.xml, и дали тој партнер е избришан во организацијата на клиентот
    250 со EARH-4001фактурата не ја поминала проверката пред УЈПсекој проблем е наведен во validationErrors, со своја шифра EARH-5xxx; поправи го полето во invoice.xml
    250 со EARH-4002УЈП ја одбил фактуратапричината е во ujp.code и ujp.message; значењето на шифрата е во Шифри од УЈП
    250 со EARH-4004обработката паднала на технички неуспех⛔ прво погледни failedStep: ако е ConvertFormat, документот не можел да се конвертира и причината е во твоето тело; ако е друг чекор, пиши на поддршката и прати го docUid

    Фактура во 250 не продолжува сама. По поправката прати нова фактура — чекорите 3 и 4 со нов број на фактура и нова X-Idempotency-Key.


Сторнирањето е можно само додека фактурата не стигнала до УЈП. Штом УЈП ја прифатил, кај нив постои запис со правно дејство и ниту еден наш повик не го брише.

Барање · ова го извршуваш ти
curl -i -X POST "https://sandbox.api.earhiva.mk/api/integration/v1/outbound/<docUid>/cancel" \
-H "X-Api-Key: <ВАШИОТ КЛУЧ>" \
-H "X-Software-Id: <ИМЕ И ВЕРЗИЈА НА ВАШАТА АПЛИКАЦИЈА>"
Состојба на фактуратаОдговорШто значи
210 или 220 — примена, обработката трча200 со "status": 260обработката е запрена; фактурата не заминува кон УЈП
250 — застаната пред УЈП200 со "status": 260документот никогаш не заминал, па сторнирањето е чисто
230, 240 или 270 — веќе кај УЈП409 EARH-3003доцна е за сторно; исправката оди преку одобрение кај УЈП
веќе сторнирана409 EARH-3004не е грешка во твојот код — веќе е направено
непознат docUid404 EARH-3001провери го идентификаторот

📌 Сторнирањето не брише: документот и понатаму се чита по својот docUid, а преминот кон 260 останува во историјата. Тоа е намерно — трагата мора да остане.


Ако чекорот 6 е исполнет, твојот софтвер праќа е-фактури кон УЈП преку eArhiva.mk и го добива УЈП ID-то на секоја. Истите четири повици — ping, POST outbound, GET outbound/{docUid}, GET outbound/{docUid}/history — се целото што ти треба за основниот тек. Во продукција се менува само адресата и клучот.

📌 Prefer: wait го чека само поднесувањето кон УЈП. Подоцнежните промени кај УЈП — примачот ја прифатил или одбил фактурата — не се чекаат; тие се читаат со GET outbound/{docUid}. УЈП ID-то стои и во списокот GET outbound, во полето ujpEuid.

Кога ти требаОди на
кој статус што значи и кон што преминуваИзлезни документи
свои фактури наместо примерот — што смееш да смениш и што е задолжителноФормати
значењето на секоја шифра EARH-xxxxКаталог на грешки
список на пратените фактури, преземање на фајл, сторноAPI Reference
колку повици во минута смеешОграничувања и правила