Публичното API на 4invoices изпраща данните за фактуриране от вашата система към 4invoices и придвижва фактурата докрай в националната система за е-фактуриране (в Румъния SPV на ANAF). Един продукционен хост, един client_credentials поток, идемпотентни фискални записи и един и същ формат на грешките по всички маршрути.
https://api.4invoices.net/v1 · OpenAPI спецификация (без ключ)
Какво е 4invoices API
- Публичният API на 4invoices е REST през HTTPS, с JSON на входа и JSON на изхода.
- Един продукционен хост: https://api.4invoices.net/v1. Отделен тестов хост няма.
- Спецификацията OpenAPI 3.1 е публична и без ключ — импортирайте я директно в Postman или в генератор на клиенти.
- Всеки отговор съдържа заглавка X-Request-Id; посочвайте я, когато се обръщате към поддръжката.
- Всеки фискален запис е идемпотентен: повторен опит след таймаут никога не издава втори документ.
- Пълният румънски цикъл с ANAF e-Factura през API: предварителна проверка, подаване, проследяване, подписан отговор и входяща пощенска кутия. Нито един румънски конкурент не предлага това.
- SmartBill няма нито един ендпойнт за подаване в националната система за е-фактуриране (ANAF, в Румъния) — изпращането към SPV е настройка на акаунта само в интерфейса — и изобщо няма ендпойнт за списък с фактури.
- Oblio може да създава клиенти и продукти само като страничен ефект от издаването на документ, а не като самостоятелни ресурси.
- Генериране на XML UBL (в Румъния CIUS-RO) без подаване: получавате правния документ като файл, без да докосвате националната система за е-фактуриране.
- Идемпотентност на фискалните записи: повторен опит след таймаут не създава втора фактура и не изразходва втори номер.
- Програмно четима входяща кутия с фактури от доставчици — основата за автоматично равнение.
Първата фактура в 6 стъпки
- Генерирайте идентификационни данни в панела, в Фактуриране → Настройки → API достъп, и копирайте client_secret — показва се само веднъж.
- Попълнете профила на фирмата и активирайте поне една серия от тип fiscale, иначе издаването връща issuer_profile_incomplete или no_series_configured.
- Генерирайте идентификационни данни в панела, в Фактуриране → Настройки → API достъп, и копирайте client_secret — показва се само веднъж.
- Попълнете профила на фирмата и активирайте поне една серия от тип fiscale, иначе издаването връща issuer_profile_incomplete или no_series_configured.
- Разменете идентификационните данни за токен: POST /v1/auth/token с grant_type client_credentials. Токенът е валиден един час — кеширайте го, не вземайте нов за всяка заявка.
- Издайте фактурата: POST /v1/invoices със заглавка Idempotency-Key и vat_rate като процент (21, не 0.21).
- Направете предварителната проверка (в Румъния CIUS-RO) с POST /v1/invoices/{id}/efactura/validate и изпратете фактурата към националната система за е-фактуриране (в Румъния ANAF) с POST /v1/invoices/{id}/efactura/submit.
- Следете състоянието с GET /v1/efactura/submissions/{id}, докато стигне accepted или rejected.
bash
# 1 — токен (1 час, кеширайте го)
ACCESS_TOKEN=$(curl -sS -X POST https://api.4invoices.net/v1/auth/token \
-H 'Content-Type: application/json' \
-d '{"grant_type":"client_credentials","client_id":"cli_XXX","client_secret":"sec_XXX"}' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')
# 2 — издаване на фактурата (Idempotency-Key е ЗАДЪЛЖИТЕЛЕН)
curl -sS -X POST https://api.4invoices.net/v1/invoices \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-10457-invoice' \
-d '{"buyer":{"name":"Exemplu Comert SRL","tax_id":"RO12345678","county":"Cluj","city":"Cluj-Napoca","country":"RO"},
"lines":[{"description":"Abonament SaaS","quantity":1,"unit_price":400,"vat_rate":21}]}'
# 3 — preflight CIUS-RO, then send to ANAF
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate \
-H "Authorization: Bearer $ACCESS_TOKEN"
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Idempotency-Key: efactura-inv_01J7ABC-1'javascript
const BASE = "https://api.4invoices.net/v1";
const token = await fetch(`${BASE}/auth/token`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "client_credentials",
client_id: process.env.FOURINVOICES_CLIENT_ID,
client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
}),
}).then((r) => r.json()).then((d) => d.access_token);
const invoice = await fetch(`${BASE}/invoices`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${orderId}-invoice`,
},
body: JSON.stringify({
buyer: { name: "Exemplu Comert SRL", tax_id: "RO12345678", county: "Cluj", city: "Cluj-Napoca", country: "RO" },
lines: [{ description: "Abonament SaaS", quantity: 1, unit_price: 400, vat_rate: 21 }],
currency: "RON",
metadata: { order_id: String(orderId) },
}),
}).then((r) => r.json());
console.log(invoice.number, invoice.totals); // "FCT-000123" { net, vat, gross }python
import os, uuid, requests
BASE = "https://api.4invoices.net/v1"
token = requests.post(f"{BASE}/auth/token", json={
"grant_type": "client_credentials",
"client_id": os.environ["FOURINVOICES_CLIENT_ID"],
"client_secret": os.environ["FOURINVOICES_CLIENT_SECRET"],
}, timeout=30).json()["access_token"]
headers = {
"Authorization": f"Bearer {token}",
# По един ключ за всяка ЛОГИЧЕСКА операция — използвайте го повторно при опит, никога не го генерирайте наново.
"Idempotency-Key": f"order-{order_id}-invoice",
}
invoice = requests.post(f"{BASE}/invoices", headers=headers, json={
"buyer": {"name": "Exemplu Comert SRL", "tax_id": "RO12345678",
"county": "Cluj", "city": "Cluj-Napoca", "country": "RO"},
"lines": [{"description": "Abonament SaaS", "quantity": 1,
"unit_price": 400, "vat_rate": 21}], # процент, не 0.21
"currency": "RON",
}, timeout=60).json()
print(invoice["number"], invoice["totals"])Автентикация
- Потокът е client_credentials: разменяте client_id + client_secret срещу Bearer access_token.
- Токенът изтича след 3600 секунди. Опреснявайте го преди изтичане, а не при всяко извикване — POST /auth/token има собствен лимит по IP.
- client_secret се съхранява само като хеш и не може да бъде възстановен. Ако го загубите, създавате нови идентификационни данни и отменяте старите.
- Само сървър-към-сървър. През CORS не се допуска нито един браузър origin: от JavaScript в страницата тайната би изтекла към посетителя.
- Обхвати се предоставят за всеки комплект идентификационни данни поотделно. Издайте отделни идентификационни данни за всяка система и дайте на всяка минималните обхвати, от които се нуждае.
Фактуриране → Настройки → API достъп
bash
curl -sS -X POST https://api.4invoices.net/v1/auth/token \
-H 'Content-Type: application/json' \
-d '{
"grant_type": "client_credentials",
"client_id": "cli_XXXXXXXXXXXX",
"client_secret": "sec_XXXXXXXXXXXXXXXXXXXX"
}'
# → {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600,
# "scope":"invoices:read invoices:write efactura:submit"}javascript
// Кеширайте токена; не създавайте нов при всяка заявка.
let cached = { token: "", expiresAt: 0 };
async function getToken() {
if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;
const res = await fetch("https://api.4invoices.net/v1/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "client_credentials",
client_id: process.env.FOURINVOICES_CLIENT_ID,
client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(`token ${res.status}`);
const data = await res.json();
cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}ДДС ставките са проценти
vat_rate: 21 ✓ · vat_rate: 0.19 ✗ · vat_rate: 1 ✗- vat_rate е ПРОЦЕНТ: 21 означава 21%.
- Стойности в (0, 1] се отхвърлят — 0.19 е дроб, а 1 е двусмислено между 1% и 100%.
- 0 е валидна стойност и означава освобождаване или обратно начисляване; поставете законовото основание в бележки.
- unit_price е НЕТНАТА единична цена, когато издателят е регистриран по ДДС.
- Трябват ви сумите, преди да запишете? POST /v1/invoices/preview-totals прави същите изчисления, без да изразходва фискален номер.
Идемпотентност при фискални записи
- Заглавката Idempotency-Key е ЗАДЪЛЖИТЕЛНА за POST /invoices, POST /invoices/{id}/storno и POST /invoices/{id}/efactura/submit.
- Ключовете се пазят 24 часа. Повторно изпращане на същия ключ със същото тяло връща първоначалния резултат и задава Idempotency-Replayed: true.
- Същият ключ с различно тяло връща 409 idempotency_key_reuse — вече издаден фискален документ никога не се презаписва.
- Един ключ за ЛОГИЧЕСКА операция, а не за HTTP опит: order-10457-invoice, а не нов UUID при всеки повторен опит.
- Причината е правна, не техническа: изразходван фискален номер и фактура, подадена в националната система за е-фактуриране (в Румъния SPV), не могат да бъдат оттеглени.
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-10457-invoice' \
-d '{
"buyer": {
"name": "Exemplu Comert SRL",
"tax_id": "RO12345678",
"address": "Str. Fabricii 12",
"city": "Cluj-Napoca",
"county": "Cluj",
"country": "RO",
"email": "[email protected]"
},
"lines": [
{ "description": "Abonament SaaS, iulie", "quantity": 1, "unit_price": 400, "vat_rate": 21, "unit": "buc" },
{ "description": "Implementare", "quantity": 6, "unit_price": 250, "vat_rate": 21, "unit": "ora" }
],
"currency": "RON",
"document_kind": "factura",
"due_date": "2026-08-15",
"metadata": { "order_id": "10457" }
}'Всички endpoint-и
| Метод | Endpoint | Scope | Idempotency-Key | Заявки / минута |
|---|---|---|---|---|
| POST | /auth/token Размяна на идентификационните данни за токен за достъп | — | — | 120 |
| GET | /me Наемател, scope-ове и текущи лимити | invoices:read | — | 120 |
| POST | /invoices Издаване на фактура или проформа | invoices:write | задължително | 30 |
| GET | /invoices Списък с фактури (страниране с курсор) | invoices:read | — | 120 |
| POST | /invoices/preview-totals Изчисляване на сумите, без да се създава нищо | invoices:read | — | 120 |
| GET | /invoices/{invoice_id} Извличане на една фактура | invoices:read | — | 120 |
| GET | /invoices/{invoice_id}/pdf Изтегляне на PDF на фактурата | invoices:read | — | 120 |
| GET | /invoices/{invoice_id}/xml Изтегляне на UBL XML (в Румъния CIUS-RO) | invoices:read | — | 120 |
| POST | /invoices/{invoice_id}/storno Издаване на кредитно известие, сторниращо тази фактура | invoices:write | задължително | 30 |
| POST | /invoices/{invoice_id}/payments Маркиране на фактурата като платена | invoices:write | — | 30 |
| DELETE | /invoices/{invoice_id}/payments Маркиране на фактурата като неплатена | invoices:write | — | 30 |
| GET | /clients Списък с клиенти | clients:read | — | 120 |
| POST | /clients Създаване или актуализиране на клиент | clients:write | — | 30 |
| GET | /lookup/company/{tax_id} Валидиране на CUI в регистъра на ANAF (Румъния) | clients:read | — | 120 |
| GET | /series Списък с серии за номериране | invoices:read | — | 120 |
| POST | /invoices/{invoice_id}/efactura/validate Предварителна проверка (в Румъния CIUS-RO) — валидиране без изпращане | efactura:read | — | 120 |
| POST | /invoices/{invoice_id}/efactura/submit Изпращане на фактурата до националната система за е-фактуриране (в Румъния ANAF/SPV) | efactura:submit | задължително | 10 |
| GET | /efactura/submissions Списък с подаванията към националната система за е-фактуриране | efactura:read | — | 120 |
| GET | /efactura/submissions/{submission_id} Извличане на едно изпращане | efactura:read | — | 120 |
| POST | /efactura/submissions/{submission_id}/refresh Запитване към националната система за е-фактуриране за текущото състояние | efactura:read | — | 10 |
| GET | /efactura/submissions/{submission_id}/download Изтегляне на архива на подписания отговор | efactura:read | — | 120 |
| GET | /efactura/inbox Фактури от доставчици, получени през националната система за е-фактуриране | efactura:read | — | 120 |
| GET | /efactura/inbox/{inbound_id} Извличане на една входяща фактура | efactura:read | — | 120 |
| GET | /efactura/inbox/{inbound_id}/download Изтегляне на архива на входящата фактура | efactura:read | — | 120 |
| POST | /efactura/inbox/sync Изтегляне на новите съобщения от националната система за е-фактуриране във входящата кутия | efactura:read | — | 10 |
25 endpoints · https://api.4invoices.net/v1/openapi.json
Scope-ове
- invoices:read
- invoices:write
- clients:read
- clients:write
- efactura:read
- efactura:submit
Пътят до националната система за е-фактуриране
- Истинското предимство: целият цикъл с националната система за е-фактуриране (в Румъния ANAF e-Factura) минава през API, а не само генерирането на документа.
- POST /invoices/{id}/efactura/validate изпълнява предварителната проверка (в Румъния CIUS-RO) и връща точно полетата, които националната система за е-фактуриране би отхвърлила, без да изпраща нищо.
- GET /invoices/{id}/xml генерира XML във формат UBL (в Румъния CIUS-RO) без подаване — полезно за собствен архив или вътрешен одит.
- POST /invoices/{id}/efactura/submit я подава в националната система за е-фактуриране (в Румъния SPV). Това е необратимо: второ копие на същата фактура не може да бъде оттеглено от системата.
- Жизненият цикъл е нормализиран: not_sent → валидиране → в опашка → изпратен → обработка → accepted | rejected | грешка. accepted означава законово предадена; грешка е транспортна грешка от наша страна и повторният опит е безопасен.
- GET /efactura/submissions/{id}/download изтегля архива с подписания отговор на националната система (в Румъния ANAF).
- Има и входяща страна: POST /efactura/inbox/sync изтегля новите съобщения от националната система за е-фактуриране (в Румъния SPV), а GET /efactura/inbox изброява фактурите, които доставчиците са ви изпратили.
bash
# CIUS-RO preflight. Sends NOTHING to ANAF.
curl -sS -X POST "https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate?include_xml=true" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# → {"invoice_id":"inv_01J7ABC","valid":false,
# "errors":["buyer.county is required for RO buyers"],
# "xml_base64":"PD94bWwg..."}bash
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Idempotency-Key: efactura-inv_01J7ABC-1'
# → {"submission_id":"sub_01J7XYZ","status":"queued"}python
import time, requests
BASE = "https://api.4invoices.net/v1"
TERMINAL = {"accepted", "rejected"}
H = {"Authorization": f"Bearer {token}"}
def wait_for_anaf(submission_id, timeout=900):
deadline = time.time() + timeout
delay = 15
while time.time() < deadline:
r = requests.get(f"{BASE}/efactura/submissions/{submission_id}", headers=H, timeout=30)
r.raise_for_status()
status = r.json()["status"]
if status in TERMINAL:
return r.json()
# /refresh asks ANAF directly and shares the 10/min submit bucket.
requests.post(f"{BASE}/efactura/submissions/{submission_id}/refresh", headers=H, timeout=30)
time.sleep(delay)
delay = min(delay * 2, 120)
raise TimeoutError(submission_id)Грешки
- Всеки 4xx и 5xx използва един и същ плик: { "error": { type, code, message, param?, doc_url, request_id, грешки? } }.
- type е общият клас, по който разклонявате логиката („мога ли да опитам отново?“), а code е стабилната причина, която съпоставяте в собствената си система.
- param е пътят с точки до проблемното поле, например lines.0.vat_rate.
- грешки[] се появява, когато една заявка има проблеми в повече от едно поле.
- request_id се записва в логовете от наша страна. Посочвайте го във всеки сигнал към поддръжката — той идентифицира точно заявката.
- 404 се връща и за id, което принадлежи на друг tenant: никога не потвърждаваме, че чужди данни съществуват.
json
{
"error": {
"type": "invalid_request_error",
"code": "validation_failed",
"message": "Datele trimise nu sunt valide.",
"param": "lines.0.vat_rate",
"doc_url": "https://4invoices.net/ro/api-dezvoltatori#errors",
"request_id": "req_8a893f2f33d4472da8d6e144",
"errors": [
{ "param": "lines.0.vat_rate", "message": "VAT rate must be a percent (21), not a fraction", "type": "value_error" }
]
}
}| Статус | Тип | Код |
|---|---|---|
| 401 | authentication_error | missing_bearer_token |
| 401 | authentication_error | invalid_client |
| 401 | authentication_error | token_expired |
| 401 | authentication_error | invalid_token |
| 401 | authentication_error | unauthorized |
| 403 | permission_error | insufficient_scope |
| 403 | permission_error | forbidden |
| 404 | not_found_error | not_found |
| 409 | conflict_error | idempotency_key_reuse |
| 409 | conflict_error | idempotency_in_progress |
| 409 | conflict_error | no_series_configured |
| 422 | invalid_request_error | validation_failed |
| 422 | invalid_request_error | issuer_profile_incomplete |
| 422 | invalid_request_error | cius_ro_incomplete |
| 422 | invalid_request_error | cannot_storno_draft |
| 422 | invalid_request_error | nothing_to_storno |
| 422 | invalid_request_error | invalid_json_number |
| 429 | rate_limit_error | rate_limit_exceeded |
| 502 | upstream_error | upstream_error |
| 503 | service_unavailable_error | service_unavailable |
Лимити на заявките
| Bucket | Заявки / минута | Какво значи |
|---|---|---|
| read | 120 | GET *, POST /invoices/preview-totals, POST /invoices/{id}/efactura/validate |
| write | 30 | POST /invoices, POST /invoices/{id}/storno, POST|DELETE /invoices/{id}/payments, POST /clients |
| submit | 10 | POST /invoices/{id}/efactura/submit, POST /efactura/submissions/{id}/refresh, POST /efactura/inbox/sync |
- Лимитите са за комплект идентификационни данни, на минута: 120 за четене, 30 за запис, 10 за операциите с националната система за е-фактуриране (submit, refresh, inbox/sync).
- X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset присъстват във ВСЕКИ отговор, включително в 429.
- 429 съдържа и Retry-After в секунди. Спазвайте го; не повтаряйте в плътен цикъл.
- Кошницата от 10/min съществува, защото в Румъния ANAF позволява приблизително едно извикване за списък със съобщения на минута за CIF.
- Пускайте /efactura/inbox/sync по cron, а не с агресивно полинг.
- Трябват ви по-високи тавани? Пишете ни чрез Контакт с очаквания дневен обем.
X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset · Retry-After
http
HTTP/2 429
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785312000
Retry-After: 17
Content-Type: application/json
{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded",
"message":"...","doc_url":"https://4invoices.net/ro/api-dezvoltatori#errors",
"request_id":"req_..."}}javascript
// Спазвайте Retry-After; никога не опитвайте отново 4xx, различен от 429.
async function call(url, init, attempt = 0) {
const res = await fetch(url, init);
if (res.status === 429 || res.status >= 500) {
if (attempt >= 5) return res;
const retryAfter = Number(res.headers.get("Retry-After")) || 2 ** attempt;
await new Promise((r) => setTimeout(r, retryAfter * 1000));
return call(url, init, attempt + 1);
}
return res;
}
// Повторен опит за запис ТРЯБВА да използва СЪЩИЯ Idempotency-Key, иначе издавате
// втори фискален документ.Ръководства по тема
Всяка тема има собствена страница със съответните endpoint-и, примери и грешки.
- Автентикация в 4invoices APIРазменяте client_id и client_secret за Bearer токен с валидност един час, само сървър-към-сървър.
- Издаване на фактури през APIPOST /invoices с Idempotency-Key, ДДС в проценти, плюс списък, сторно и маркиране като платена.
- Клиенти и проверка в регистъра на фирмитеВмъкване или актуализиране с дедупликация по tax_id и справка в регистъра на ANAF (Румъния), за да не бъде отхвърлена е-фактурата.
- Национална система за е-фактуриране през APIПредварителна проверка (в Румъния CIUS-RO), подаване в националната система (в Румъния SPV), нормализиран статус, подписан отговор и входяща кутия с фактури.
- Серии за номериране в APIБез активна серия от тип fiscale издаването връща 409 no_series_configured.
- Кодове за грешки и форматът на грешкатаЕдин формат по всички маршрути: type, code, message, param, doc_url, request_id и грешки.
- Лимити на заявките и повторни опити120 четения, 30 записа и 10 операции с националната система за е-фактуриране на минута за идентификатор, с X-RateLimit-* хедъри.
Често срещани грешки
- Изпращане на ДДС като дроб (0.19) или като 1. И двете се отхвърлят; изпращайте 19 или 21.
- Създаване на токен за всяка заявка. Токенът е валиден един час; кеширайте го, иначе ще ударите лимита по IP на токен endpoint-а.
- Генериране на нов идемпотентност ключ при всеки повторен опит — точно сценарият, който създава дублирани фактури.
- Извикване от JavaScript в браузъра. CORS блокира всеки източник, а тайната така или иначе би била разкрита.
- Подаване в националната система за е-фактуриране без предварителната проверка (в Румъния CIUS-RO). Грешно подаване не може да бъде оттеглено.
- Полинг на /efactura/submissions/{id}/refresh повече от 10 пъти в минута — тази кошница е обща със изпращам.
- Издаване, преди профилът на фирмата да е попълнен и да съществува активна фискална серия.
Въпроси и отговори за разработчици
Как получавам данни за достъп до 4invoices API?
В панела, в Фактуриране → Настройки → API достъп. Получавате client_id и client_secret; secret-ът се показва еднократно и се пази само като хеш.
Колко време е валиден access_token?
3600 секунди, тоест един час. Кеширайте го и го подновявайте преди изтичане — токен endpoint-ът има собствен лимит по IP.
Как да изпратя ДДС ставката?
Като процент: 21 за 21%. Стойности в (0, 1] се отхвърлят, така че 0.19 и 1 дават грешка при валидация.
Кои заявки изискват Idempotency-Key?
POST /invoices, POST /invoices/{id}/storno и POST /invoices/{id}/efactura/submit. Ключовете се помнят 24 часа, а повторение връща оригиналния документ с Idempotency-Replayed: true.
Мога ли да подам фактура към националната система за е-фактуриране директно през API?
Да. Предварителна проверка с /efactura/validate (в Румъния CIUS-RO), подаване с /efactura/submit, следене на нормализирания статус на /efactura/submissions/{id} и изтегляне на подписания отговор на системата (в Румъния ANAF).
Какви са лимитите на заявките?
На идентификационни данни, за минута: 120 четения, 30 записа и 10 операции с националната система за е-фактуриране. Всеки отговор носи X-RateLimit-* заглавки, а 429 добавя Retry-After.
Мога ли да извиквам API от фронтенда?
Не. Само сървър-към-сървър е: никакъв браузърен източник не минава CORS, а client_secret няма място в код, който стига до посетителя.
Защо получавам issuer_profile_incomplete на първата фактура?
Профилът на фирмата е непълен или няма активна серия от тип fiscale. Допълнете ги в панела; API не издава невалиден фискален документ.