BFakturaВход с BMail

API за фактури

Вашият сайт, магазин или програма може да издава фактури и проформи в BFaktura сама: с правилен номер, ДДС по ставки и PDF, и да ги праща на купувача по имейл. Нужни са акаунт в BMail и попълнен профил на фирмата.

Начало

Създайте ключ в Интеграции. Ключовете са два вида: bf_test_… за проба и bf_live_… за работа. Ключът се показва само веднъж; пазете го като парола, на сървъра, не в браузъра.

Всички адреси започват с https://faktura.bizzupp.bg/api/v1. Ключът се праща в заглавка Authorization: Bearer <ключ>. Данните са JSON с UTF-8, суми в евро, дати във вид 2026-10-07. Проверка дали ключът работи: GET /api/v1/key.

Нов документ

Една заявка създава документа и, ако поискате, го издава и праща. Създаването и издаването са едно цяло: или документът е издаден, или нищо не е записано и отговорът казва кое поле да се поправи.

curl https://faktura.bizzupp.bg/api/v1/documents \
  -H "Authorization: Bearer bf_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001" \
  -d '{
    "type": "INVOICE",
    "issue": true,
    "send": true,
    "externalId": "1001",
    "recipient": {
      "name": "Строй Комплект ООД",
      "eik": "121212121",
      "vatNumber": "BG121212121",
      "address": "София, бул. Витоша 1",
      "mol": "Петър Петров",
      "email": "orders@stroi.bg"
    },
    "lines": [
      { "description": "Боя фасадна 15 л", "unit": "бр.", "quantity": 2, "unitPrice": 54, "vatRate": 20 }
    ],
    "pricesIncludeVat": true,
    "paymentMethod": "CARD",
    "paidOn": "2026-10-07"
  }'

Отговорът е 201 с документа. Повторената заявка връща 200 със същия документ и "duplicate": true.

{
  "id": 412,
  "type": "INVOICE",
  "status": "ISSUED",
  "number": "0000000123",
  "issueDate": "2026-10-07",
  "taxBase": 90.00,
  "vatAmount": 18.00,
  "total": 108.00,
  "paidOn": "2026-10-07",
  "source": "API",
  "externalId": "1001",
  "pdfUrl": "https://faktura.bizzupp.bg/api/v1/documents/412/pdf",
  …
}

Същото на PHP

$ch = curl_init('https://faktura.bizzupp.bg/api/v1/documents');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('BFAKTURA_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: order-' . $order['id'],
    ],
    CURLOPT_POSTFIELDS => json_encode($invoice, JSON_UNESCAPED_UNICODE),
]);
$document = json_decode(curl_exec($ch), true);

Полета

type
INVOICE или PROFORMA. Кредитното известие има свой адрес.
issue
true издава веднага с номер; без него остава чернова, която издавате по-късно.
send
true праща издадения документ на recipient.email от faktura@bizzupp.bg. Отговорите на купувача отиват на имейла от профила ви.
externalId
Номерът на поръчката във вашата система, до 100 знака. Пази от двойни документи.
recipient
name и address са задължителни за издаване; eik, vatNumber, mol, email по желание. Физическо лице е само с име и адрес.
lines
До 200 реда с description, unit (по подразбиране „бр.“), quantity (до 3 знака), unitPrice и vatRate (0, 9 или 20). Ако профилът не е по ЗДДС, ставката е 0 и основанието се пише само.
pricesIncludeVat
true, ако цените са крайни, с ДДС. Тогава цената без ДДС се смята с 4 знака; сборът може да се различава от платеното с до 0,01 € за ставка.
paymentMethod
BANK (иска IBAN в профила), CASH, CARD или OTHER с paymentNote.
paidOn
Дата на плащане; само за фактура, която се издава.
vatNote
Основанието при ставка 9% или 0%, например „чл. 41 от ЗДДС“.
issueDate, taxEventDate, dueDate
По подразбиране днес и падежът от настройките ви. Номерата вървят по датите, затова документ не може да е с дата преди последния издаден.
template, language
Шаблонът от „Дизайн“ (CLASSIC, MODERN и др.) и езикът: BG или BG_EN за двуезичен документ.

Повторения

Мрежата понякога прекъсва и заявката се праща пак. За да не излязат две фактури за една поръчка, пращайте externalId или заглавка Idempotency-Key:

  • Същият externalId винаги връща съществуващия документ.
  • Същият Idempotency-Key до 24 часа връща същия отговор. Със същия ключ и друго тяло отговорът е 409.

Проба

С ключ bf_test_… заявката минава всички проверки като истинската и връща документа с "test": true и PDF в pdfBase64 с надпис „ПРОБА“. Нищо не се записва, не се взима номер и не се праща писмо. Другите адреси с пробен ключ връщат 403.

Други адреси

  • GET /documents: списък, най-новите първо. Филтри type, status, from, to, externalId; страници с before=<id> и limit до 100.
  • GET /documents/{id} и GET /documents/{id}/pdf.
  • POST /documents/{id}/issue: издава чернова.
  • POST /documents/{id}/paid с {"paidOn": "2026-10-07"} и DELETE /documents/{id}/paid.
  • POST /documents/{id}/credit-note с reason, по желание lines (за частично известие) и issue.
  • POST /documents/{id}/send, по желание с {"email": "…"}.

Грешки и граници

Грешката е JSON с error (за хора, на български), code (invalid, unauthorized, forbidden, not_found, conflict, rate_limited) и при грешни полета fields, например {"lines.0.quantity": "Количеството трябва да е над нула"}.

  • 60 заявки в минута на ключ; след тях 429 с Retry-After.
  • До 500 издадени документа за 24 часа през API и връзките, до 200 писма. Пишете ни, ако ви трябват повече.

Известия

В Интеграции задайте https адрес и събитията: document.issued, credit_note.issued, document.paid, document.unpaid, email.sent, email.failed. BFaktura праща POST с JSON. Известия идват и за документите, издадени в самото приложение.

{
  "id": "evt_5f0c…",
  "type": "document.issued",
  "created": 1791370800,
  "data": {
    "document": { "id": 412, "number": "0000000123", "total": 108.00, "externalId": "1001", … }
  }
}

Отговорете с код 2xx до 10 секунди. Иначе опитваме пак след 1 и 5 минути, след 30 минути и после след 2, 6 и 24 часа. Всяко известие е подписано в заглавка BFaktura-Signature: t=<време>,v1=<подпис>: HMAC-SHA256 на време.тяло с тайната на адреса. Проверете го, преди да вярвате на известието:

import crypto from "node:crypto";

// header е стойността на BFaktura-Signature, body е суровият текст на заявката.
function verify(secret, header, body) {
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${body}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
function verify(string $secret, string $header, string $body): bool
{
    parse_str(str_replace(',', '&', $header), $parts);
    $expected = hash_hmac('sha256', $parts['t'] . '.' . $body, $secret);
    return abs(time() - (int) $parts['t']) < 300 && hash_equals($expected, $parts['v1']);
}

Връзки без код

За WooCommerce, Shopify и Stripe не е нужно програмиране. В Интеграции създайте връзка, копирайте адреса в платформата и поставете ключа за подпис. Новата връзка е в проба: поръчките само се проверяват и се виждат в дневника. Когато излизат както трябва, я пуснете на живо.

  • WooCommerce: издава при избрания статус на поръчката; ЕИК, ДДС номер и МОЛ се вземат от полетата на българските добавки за фактури.
  • Shopify: издава при платена поръчка; ЕИК и ДДС номер се вземат от допълнителните полета на поръчката.
  • Stripe: издава при завършено плащане в Checkout и при платена фактура на Stripe.

Ако поръчка не може да се издаде (например липсва адрес), тя остава чернова и в дневника пише защо. Въпроси: info@bmail.bg.