Nordlet

← Документация / Ръководства

API конвенции

Структура на заявките, обхвати на удостоверяване, списъчни заявки, парични суми, грешки, идемпотентност, лимити на заявките.

Структура на заявките

Всяка операция е с RPC стил и се изпълнява чрез POST:

POST /v1/{module}/{resource}/{action}

Модули: reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Действията са глаголи: create, get, update, delete, list, както и специфични за домейна действия като issue, register, match, apply-advance, generate.

Удостоверяване и обхвати (scopes)

Authorization: Bearer <api key>. Ключовете носят обхвати, които се проверяват за всяко действие:

  • {module}:read - действия за извличане и извеждане на списъци (get/list)
  • {module}:write - модифициращи операции
  • {module}:* или * - общи маски (wildcards)

Липсващ обхват води до 403 forbidden. Липсващ или невалиден ключ води до 401 unauthorized.

Списъчни заявки (list queries)

Всички действия от тип list споделят единен договор:

{
  "page": 1,
  "pageSize": 50,
  "sort": [{ "field": "createdAt", "dir": "desc" }],
  "filter": [{ "field": "paymentStatus", "op": "eq", "value": "unpaid" }]
}

Оператори за филтриране: eq (равно), ne (не е равно), contains (търсене на текст без чувствителност към регистъра на буквите, само за текстови полета), gte (по-голямо или равно), lte (по-малко или равно), in (съвпадение с която и да е стойност от списък с до 500 елемента). Полетата, които поддържат сортиране и филтриране, са предварително разрешени за всеки краен пункт (вижте спецификацията OpenAPI); непознатите полета връщат грешка 400. Добавете totals: ["field", …] към заявката, за да сумирате числовите полета за всеки съответстващ ред; когато записите съдържат валута, totalsByCurrency предоставя същите суми, разпределени по валутен код. Отговори: { rows, total, page, pageSize, totals?, totalsByCurrency? }.

Парични суми и дати

Паричните стойности се представят като десетични низове с до 4 знака след десетичната запетая ("121.0000"), като никога не се използват числа с плаваща запетая (floats). ДДС се изчислява за всеки ред поотделно с коефициент на закръгляване нагоре при полусума (half-up rounding). Датите са във формат YYYY-MM-DD, а времевите отпечатъци (timestamps) са в ISO 8601 UTC.

Грешки

Унифициран формат за отговор при грешка:

{
  "error": {
    "code": "validation",
    "message": "Request validation failed",
    "requestId": "k1x…",
    "key": "request_invalid",
    "fieldErrors": { "lines.0.unitPriceExclVat": ["Invalid value"] }
  }
}

Кодове: validation (400/422, както и 413, когато каченият файл или тялото на заявката са прекалено големи), unauthorized (401), payment_required (402, пробният период или предоплатеният кредит са изтекли; съобщението описва как да продължите), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Когато съобщението е едно от предварително дефинираните, key го идентифицира, за да може клиентското приложение да покаже собствен превод, а params съдържа динамичните стойности за него (например { "period": "2026-09" } за заключен счетоводен период). Включвайте идентификатора requestId, когато докладвате възникнали проблеми.

Идемпотентност

Добавете Idempotency-Key: <произволен низ с дължина до 255 знака> към всяка модифицираща заявка:

  • Повторен опит със същия ключ и полезен товар (payload) - съхраненият отговор се възпроизвежда до байт точно с хедър x-idempotent-replay: true.
  • Същят ключ, но с различен полезен товар - връща се 422 idempotency_key_reuse.
  • Едновременен дубликат, докато първата заявка все още се обработва - връща се 409 idempotency_in_progress.
  • Ключовете принадлежат на API ключа или потребителя, който ги изпраща, в рамките на компанията, от чието име се прави заявката, така че две интеграции на една и съща компания могат да използват един и същ ключ. Заявките, направени преди създаването на компанията, както и account/* заявките, запазват ключовете за всеки потребител поотделно, така че повтаряща се заявка account/companies/create създава само една компания.
  • Съхраненият отговор се връща само след като извикващата страна премине отново през същите проверки за права (обхват, роля, компания и статус на плащане); в противен случай заявката получава актуалната грешка.
  • Ключовете изтичат след 24 часа. Отговорите с грешки (4xx) също се възпроизвеждат; отговорите 5xx не се съхраняват, така че при повторни опити те се изпълняват наново.

Лимити на заявките (rate limits)

300 заявки в минута за един API ключ (на база предоставени идентификационни данни; неавтентифицираните заявки се лимитират на база IP адрес). При получаване на 429 следвайте указанията в хедъра retry-after. Хедърите x-ratelimit-* предоставят информация за наличния ви лимит.

Уебхукове (webhooks)

Събитията се записват в транзакционна опашка за изходящи съобщения (outbox) в рамките на същата база данни и транзакция като самата промяна - отмененият (rolled-back) документ никога не генерира събитие и нито едно събитие не се губи. Доставка:

  • POST заявка към вашия URL адрес с JSON събитието
  • x-nordlet-timestamp: <unix seconds> - моментът, в който е подписана доставката
  • x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 на "<timestamp>.<raw body>">, генериран с вашия секрет за абонамент
  • x-nordlet-signature: sha256=<hex HMAC-SHA256 на суровото тяло>, запазен непроменен за съществуващи получатели
  • x-nordlet-delivery съдържа идентификатора на доставката, който остава същият при всеки нов опит за същата доставка
  • Отговор, който не е в диапазона 2xx, или пренасочване (redirect) води до нов опит след 30 секунди, 2 минути, 10 минути, 30 минути, 1 час, 3 часа, 6 часа и 12 часа (общо около 24 часа), преди доставката да бъде маркирана като неуспешна
  • Всяка доставка се изпраща само от един сървър едновременно, така че стартирането на няколко API инстанции няма да доведе до двойно изпращане

Проверявайте подписа x-nordlet-signature-v2 чрез сравнение с фиксирано време (constant-time comparison), преди да се доверите на полезния товар, и отхвърляйте доставки, чийто времеви отпечатък се различава с повече от 5 минути от системното ви време. Времевият отпечатък е част от подписания текст, така че заловена доставка не може да бъде възпроизведена по-късно с нов времеви отпечатък. Съхранявайте обработените идентификатори от x-nordlet-delivery, за да пренебрегвате дублирани събития в рамките на този прозорец. Неуспешната доставка може да бъде изпратена отново чрез webhooks/deliveries/redeliver.

Одит (audit)

Всяка модифицираща операция се записва (actor, action, entity, diff) и може да бъде извличана чрез заявка към audit/list.

Счетоводни гаранции

  • Журналните контировки винаги са балансирани - това се гарантира от отложен тригер в базата данни по време на фиксиране (commit), а не само от приложния код.
  • Номерата на документите са непрекъснати (gapless) в рамките на съответната серия и година, а разпределението им е безопасно при едновременен достъп (concurrency-safe).
  • Заключените счетоводни периоди отхвърлят всякакви счетоводни записвания с дата в тях (409).
  • Документите с меко изтриване (soft-deleted) запазват своята одитна пътека; издадените или регистрирани документи не могат да бъдат изтривани.