REST · JSON · v1.4

API documentation (v1)

REST API for machine-to-machine integrations: fiscalisation platforms, POS, ERP, BI and scripts.

Introduction

LlogariPro exposes a REST API through which external systems read and write a company's books without a user session. The company is determined by the API key, so URLs carry no company id.

All requests and responses are JSON (UTF-8). Dates are ISO 8601 (YYYY-MM-DD), amounts are returned as decimal strings (e.g. "1250.00") to preserve precision, and lists are paginated with page/per_page.

Base URL: https://books.konsulence.al/api/v1

Get a key under Integrations › API keys after signing in.

Authentication

Every request carries the company key in the X-API-Key header or as Authorization: Bearer <key>. Keys are created by an admin or accountant under Integrations › API keys and are shown in full only once.

A key carries scopes. A read-only key cannot create anything; a fiscal-import key can only import invoices.

X-API-Key: lp_a1b2c3d4.dGhpc19pc19hX3NlY3JldF9rZXk…
# or
Authorization: Bearer lp_a1b2c3d4.dGhpc19pc19hX3NlY3JldF9rZXk…

Scopes

Scope
readReads everything: company, chart of accounts, partners, invoices, payments, journal, projects, dimensions, reports.
writeCreates partners, invoices, payments and journal entries. Implies read and invoices:import.
invoices:importOnly the fiscal invoice import at POST /import/invoices (fiscalisation platform keys).

Conventions

Pagination?page=1&per_page=50 (max 200). Responses carry meta: {page, per_page, total, pages}.
Date filtersfrom / to for invoices, payments and journal; start / end for reports; as_of for balances.
AmountsDecimal strings with two places ("1250.00"). Numbers are also accepted in requests.
Idempotencyexternal_id on invoices and journal entries prevents duplicates; partners are matched by NIPT or name.
Rate limitUp to 120 requests per minute per key. 429 when exceeded.

Errors

Errors return an HTTP status and a JSON body {"error": "message", "code": "code"}.

400 bad_requestInvalid JSON or wrong parameter
401 unauthorizedMissing, invalid or revoked key
403 insufficient_scopeThe key lacks the required scope
404 not_foundResource does not exist in this company
409 conflictConflict (e.g. company NIPT does not match the key)
422 unprocessableInvalid accounting data: unbalanced entry, missing account, mandatory dimension
207 multi-statusImport partially succeeded; see results per invoice

Meta

GET/ping —

Verifies the key; returns the company, version and scopes.

Response
{"ok": true, "version": "1.3", "company": "Demo Alfa shpk", "nipt": "L12345678A", "scopes": ["read", "write"]}

GET/company read

The key's company profile.

Response
{"id": "…", "name": "Demo Alfa shpk", "nipt": "L12345678A", "currency": "ALL", "vat_registered": true}

Chart of accounts

GET/accounts read

List of accounts. with_balance=1 adds the balance (optionally as_of).

Parameters
class1–9
active1 (default) | 0
with_balance1 to include balances
as_ofYYYY-MM-DD
Response
{"data": [{"code": "411", "name": "Klientë", "class": "4", "normal_side": "debit", "balance": "125000.00"}], "meta": {"total": 96}}

GET/exchange-rates read

Dated exchange rates (1 foreign = rate × ALL) as stored for the company: Bank of Albania feed (source boa), manual or revaluation.

Parameters
currencyEUR, USD…
from, toYYYY-MM-DD
Response
{"home_currency": "ALL", "data": [{"currency": "EUR", "rate_date": "2026-09-04", "rate": "92.120000", "source": "boa"}], "meta": {…}}

GET/tax-rates read

VAT and other tax rates.

Response
{"data": [{"id": "…", "code": "VAT20", "name": "TVSH 20%", "kind": "vat", "rate": "20.00"}]}

GET/dimensions read

The three dimensions (cost centres, projects, business units) with their values and mode (off/optional/mandatory).

Response
{"data": [{"kind": "cost_center", "name": "Qendra e kostos", "mode": "optional", "values": [{"id": "…", "code": "ADM", "name": "Administrata"}]}]}

Partners (customers / donors / suppliers)

GET/partners read

Paginated list of partners.

Parameters
typecustomer | supplier
qsearch name/NIPT/email
niptexact match
page, per_pagepagination
Response
{"data": [{"id": "…", "name": "Klienti Sh.p.k.", "nipt": "L91234567B", "is_customer": true, "payment_terms_days": 30}], "meta": {"page": 1, "per_page": 50, "total": 1, "pages": 1}}

GET/partners/{id} read

Partner card: details, receivable/payable figures (invoiced, paid, open, overdue) and 411/401 ledger balances.

Response
{"id": "…", "name": "…", "receivable": {"invoiced": "90000.00", "paid": "60000.00", "open": "30000.00", "overdue": "0.00"}, "ledger_balances": {"411": "30000.00"}}

POST/partners write

Creates a partner. Idempotent: if the NIPT or name exists, returns the existing partner (200) widening its role.

Request
{"name": "Klienti Sh.p.k.", "nipt": "L91234567B", "is_customer": true, "is_supplier": false, "email": "info@klienti.al", "city": "Tiranë", "payment_terms_days": 30}
Response
201 → {"id": "…", "name": "Klienti Sh.p.k.", "nipt": "L91234567B", …}

Invoices and payments

GET/invoices read

Paginated invoice list with filters.

Parameters
directionsale | purchase
statusdraft | issued | paid | cancelled
stateopen | overdue | partial | paid (derived from payments/due date)
partner_id, number, external_idexact filters
from, toissue date range
Response
{"data": [{"id": "…", "direction": "sale", "number": "45/2026", "issue_date": "2026-09-01", "due_date": "2026-10-01", "total": "60000.00", "amount_paid": "0.00", "balance_due": "60000.00", "status": "issued", "state": "open"}], "meta": {…}}

GET/invoices/{id} read

Invoice with lines, dimensions and payments.

Response
{"id": "…", "number": "45/2026", "lines": [{"description": "…", "quantity": "1.000", "unit_price": "50000.0000", "vat_rate": "20.00", "account_code": "701", "line_total": "60000.00"}], "payments": []}

POST/invoices write

Creates and posts one invoice (status issued). The partner is matched by NIPT or created. Structure below.

Request
(shiko strukturën e faturës / see invoice structure)
Response
201 → fatura e plotë / the full invoice · 200 nëse external_id ekzistonte / if external_id already existed

POST/import/invoices invoices:import

Bulk import from fiscalisation platforms: {"source": "devsync", "company_nipt": "…", "invoices": [ … ]}. Each invoice is reported individually; 207 on partial errors.

Request
{"source": "devsync", "company_nipt": "L12345678A", "invoices": [ {…}, {…} ]}
Response
{"company": "…", "summary": {"created": 2, "skipped": 0, "error": 0}, "results": [{"external_id": "EIC-…", "number": "45/2026", "status": "created", "invoice_id": "…"}]}

GET/payments read

Invoice receipts and payments.

Parameters
partner_id
directionsale (receipts) | purchase (payments)
from, topayment date range
Response
{"data": [{"id": "…", "invoice_id": "…", "invoice_number": "45/2026", "pay_date": "2026-09-15", "amount": "60000.00", "account_code": "512", "realized_fx": "0.00"}], "meta": {…}}

POST/invoices/{id}/payments write

Records and books a (full or partial) payment against a posted invoice. account_code is the cash/bank account (class 5).

Request
{"amount": 60000, "account_code": "512", "pay_date": "2026-09-15", "exchange_rate": null}
Response
201 → {"id": "…", "amount": "60000.00", "journal_entry_id": "…"}

Expenses (expense manager)

GET/expense-categories read

Expense accounts (class 6) plus the category mapping the company configured — to offer employees the right categories.

Response
{"data": [{"code": "625", "name": "Udhëtime e dieta"}], "category_map": {"Udhëtime": {"account": "625"}}, "default_account": "601"}

POST/import/expenses expenses:import

Import approved expenses: {"expenses": [ … ]}. Reimbursables become a payable to the employee; company-paid ones settle from the account mapped for paid_with. Idempotent by external_id; the receipt (base64) is attached.

Request
{"expenses": [{"external_id": "EXP-42", "date": "2026-09-05", "amount": 12000, "vat_amount": 2000, "category": "Udhëtime", "description": "Hotel 2 net", "employee": {"name": "Ardit Hoxha", "email": "ardit@firma.al"}, "merchant": {"name": "Hotel Tirana"}, "payment": "reimbursable", "receipt": {"filename": "hotel.pdf", "content_base64": "…"}}]}
Response
{"company": "…", "summary": {"created": 1}, "results": [{"external_id": "expman:EXP-42", "number": "EXP-EXP-42", "status": "created", "invoice_id": "…"}], "receipts_attached": 1}

Journal

GET/journal-entries read

Journal entries; with_lines=1 includes the lines.

Parameters
sourcemanual | invoice | bank | payroll | payment | revaluation | api
from, to
with_lines1
Response
{"data": [{"id": "…", "entry_no": "202609-0001", "entry_date": "2026-09-01", "memo": "Shitje - Klienti #45/2026", "source": "invoice", "total_debit": "60000.00"}], "meta": {…}}

GET/journal-entries/{id} read

The entry with its lines, partner and dimensions.

Response
{"id": "…", "lines": [{"account_code": "411", "debit": "60000.00", "credit": "0.00", "partner_id": "…", "dimensions": [{"kind": "cost_center", "value": "Administrata"}]}]}

POST/journal-entries write

Posts a balanced manual entry. All rules apply: debit = credit, accounts must exist, mandatory dimensions on class 6/7.

Request
{"entry_date": "2026-09-30", "memo": "Qira shtator", "external_id": "ERP-JE-1001", "lines": [{"account_code": "613", "debit": 50000, "description": "Qira", "dimension_ids": ["<cost-centre-value-id>"]}, {"account_code": "512", "credit": 50000}]}
Response
201 → {"id": "…", "entry_no": "202609-0042", "posted": true, "lines": [ … ]}

Projects

GET/projects read

Projects (grants/contracts); with_actuals=1 adds actuals.

Parameters
statusplanned | active | closed
partner_id
with_actuals1
Response
{"data": [{"id": "…", "code": "P-01", "name": "Granti UNDP 2026", "partner_name": "UNDP", "status": "active", "budget_expense": "1000000.00", "actuals": {"income": "500000.00", "expense": "300000.00", "expense_pct": "30.0"}}]}

GET/projects/{id} read

Project with budget vs actual per account.

Response
{"id": "…", "budget_vs_actual": [{"account_code": "601", "budget": "1000000.00", "actual": "300000.00", "variance": "700000.00", "pct": "30.0"}]}

Reports

GET/reports/income-statement read

Income statement for the period with SKK groupings; dimension_value_id filters to one cost centre, project or unit.

Parameters
start, endYYYY-MM-DD (default: current year)
dimension_value_id
Response
{"period": {…}, "income": [ … ], "expense": [ … ], "total_income": "…", "total_expense": "…", "result_before_tax": "…", "profit_tax": "…", "result_after_tax": "…"}

GET/reports/balance-sheet read

Balance sheet as of a date.

Parameters
as_ofYYYY-MM-DD (default: today)
Response
{"as_of": "2026-09-30", "assets": …, "liabilities": …, "equity": …}

GET/reports/partner-balances read

Balances per partner on 411 (customers) or 401 (suppliers).

Parameters
account411 (default) | 401
as_of
Response
{"account": "411", "data": [{"partner_id": "…", "partner": "Klienti Sh.p.k.", "nipt": "…", "debit": "…", "credit": "…", "balance": "30000.00"}]}

Office (office key)

GET/practice/ping monitor

Verifies the office key (lpo_…). Company keys do not open these routes, and vice versa.

Response
{"ok": true, "version": "1.4", "practice": "KONSULENCE.AL", "nipt": "L32327017S", "scopes": ["monitor", "offers"], "billing_company": {"id": "…", "name": "KONSULENCE.AL shpk", "nipt": "…", "currency": "ALL", "charges_vat": true}}

GET/practice/companies monitor

The companies the office keeps (archived too, with archived: true) and who has access to each.

Response
{"data": [{"id": "…", "name": "Alfa shpk", "nipt": "L11111111A", "archived": false, "members": [{"user_id": "…", "role": "admin"}]}]}

GET/practice/members monitor

The office team.

Response
{"data": [{"id": "…", "email": "ana@zyra.al", "name": "Ana K.", "practice_role": "member"}]}

GET/practice/status monitor

State of the books per active client: uncategorised bank lines, open/overdue documents, fiscalisation, period lock, tasks and last activity.

Response
{"as_of": "2026-10-23", "data": [{"company_id": "…", "bank": {"pending_lines": 12, "oldest_pending_date": "2026-09-02"}, "books": {"last_entry_date": "2026-10-21", "lock_date": "2026-08-31"}, "invoices": {"overdue_sales": 3, "overdue_sales_amount": "48000.00"}, "fiscal": {"errors": 0, "certificate": "ok"}, "tasks": {"open": 2, "overdue": 0}}]}

GET/practice/activity monitor

Who worked where: one row per day × company × user, with active minutes and actions (manual / bulk). No user = API, cron, webhook.

Parameters
from, toYYYY-MM-DD (up to 93 days; default the last 30)
Response
{"from": "2026-10-01", "to": "2026-10-23", "users": [{"id": "…", "email": "ana@zyra.al", "name": "Ana K."}], "data": [{"date": "2026-10-20", "company_id": "…", "user_id": "…", "active_minutes": 95, "actions": 41, "manual_actions": 33, "bulk_actions": 8, "by_type": {"JournalEntry": 20}, "first_at": "…", "last_at": "…"}]}

GET/practice/tasks monitor

Open Work tasks; with done_since also the ones completed since that date.

Parameters
done_sinceYYYY-MM-DD
Response
{"data": [{"id": "…", "title": "TVSH shtator", "company_id": "…", "status": "in_progress", "assigned_to": "…", "due_date": "2026-10-15"}]}

POST/practice/offers offers

An offer for one of the office's clients (e.g. time worked, from the Client Portal), in the company that bills the clients (Office → Client Portal). The client becomes a partner by NIPT. Created not yet sent; the same external_id replaces it while it is unsent, then returns 409 offer_locked.

Parameters
external_id, partner {nipt, name, email}, issue_date, valid_until, notes, lines [{description, quantity, unit_price, vat_rate}]vat_rate defaults to 20; VAT only when the company charges it
Response
{"status": "created", "offer": {"id": "…", "external_id": "portal-offer-…", "number": "OF-2026-0007", "state": "draft", "subtotal": "6500.00", "vat_total": "1300.00", "total": "7800.00", "url": "https://books.konsulence.al/invoices/…", "invoice": null}}

GET/practice/offers offers

The state of offers by external_id (sent, accepted, turned into an invoice with the invoice number).

Parameters
external_idcomma-separated ids
Response
{"billing_company": {…}, "data": [{"external_id": "portal-offer-…", "number": "OF-2026-0007", "state": "converted", "invoice": {"number": "SH-2026-0042", "status": "draft"}}]}

Invoice structure (POST /invoices and /import/invoices)

{
  "external_id": "EIC-2026-000123",        // idempotency key (optional but recommended)
  "direction": "sale",                     // sale | purchase
  "number": "45/2026",
  "issue_date": "2026-09-01",
  "due_date": "2026-10-01",
  "currency": "ALL",                       // ISO code; exchange_rate for foreign currency
  "payment_method": "noncash",             // cash | card | noncash
  "tcr_code": "xh123abc45",                // fiscal cash register (optional)
  "nivf": "<UUID>", "nslf": "<32 hex>",    // fiscal codes (optional): NIVF = FIC, NSLF = IIC
  "qr": "…",
  "partner": {"nipt": "L91234567B", "name": "Klienti Sh.p.k."},
  "pdf_base64": "JVBERi0xLjQK…", "pdf_filename": "Fatura_45-2026.pdf",   // optional: PDF attached to the invoice
  "lines": [
    {"description": "Shërbim konsulence", "quantity": 1, "unit_price": 50000,
     "vat_rate": 20, "account_code": "701", "dimension_ids": ["<value-id>"]}
  ]
}
Field
directionsale or purchase — required
partnernipt and/or name; matched by NIPT, then name, otherwise created with the right role
lines[]description, quantity, unit_price (net of VAT), vat_rate (%), account_code (7xx sales, 6xx purchases; default 701/601), dimension_ids
payment_methodcash/card book to the TCR cash account (TCR → cash mapping); noncash to 411/401
currency, exchange_rateFor foreign currency; without a rate the dated rate table is used
external_idIdempotency key: the same invoice is never imported twice (status skipped)

curl

curl -H "X-API-Key: lp_a1b2c3d4.…" https://books.konsulence.al/api/v1/ping
curl -H "X-API-Key: $KEY" "https://books.konsulence.al/api/v1/invoices?direction=sale&state=open&per_page=100"
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  https://books.konsulence.al/api/v1/invoices \
  -d '{"external_id":"ERP-45","direction":"sale","number":"45/2026","issue_date":"2026-09-01",
       "partner":{"nipt":"L91234567B","name":"Klienti Sh.p.k."},
       "lines":[{"description":"Konsulencë","quantity":1,"unit_price":50000,"vat_rate":20,"account_code":"701"}]}'
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  https://books.konsulence.al/api/v1/invoices/<id>/payments -d '{"amount":60000,"account_code":"512","pay_date":"2026-09-15"}'

Sign in →