Skip to main content

High-level Python client for the mBoek bookkeeping API

Project description

mboek Python client

A high-level, synchronous Python client library for the mBoek bookkeeping API.

Installation

pip install mboek

Requires Python ≥ 3.10 and requests.

Quick start

from mboek import MboekClient

with MboekClient("http://localhost:3000", "admin", "geheim") as client:

    # List all company administrations
    admins = client.administraties.list()
    admin = admins[0]
    print(f"Administration: {admin.naam}  (id={admin.id})")

    # Scope all further calls to one administration
    a = client.administratie(admin.id)

    # List fiscal years
    years = a.boekjaren.list()
    boekjaar = next(y for y in years if y.status.value == "open")
    print(f"Open boekjaar: {boekjaar.naam}  ({boekjaar.start_datum}{boekjaar.eind_datum})")

    # Scope to a specific boekjaar
    bj = a.boekjaar(boekjaar.id)

    # List journals
    dagboeken = a.dagboeken.list()
    bank = next(d for d in dagboeken if d.dagboek_type.value == "bank")

    # List journal entries for the bank dagboek in this boekjaar
    entries = bj.dagboek(bank.id).boekingen.list()
    for entry in entries[:5]:
        print(f"  {entry.boeking.datum}  {entry.boeking.omschrijving}")

    # Generate a balance sheet
    balans = bj.reports.balans()
    print(f"Activa: {balans.totaal_activa}  Passiva: {balans.totaal_passiva}  In balans: {balans.in_balans}")

API hierarchy

Resources are accessed through a scoped hierarchy that mirrors the domain model:

MboekClient
├── administraties            ← cross-administration resources
├── boekingen                 ← get / update / delete by ID
├── export_import             ← import_administratie
├── maintenance               ← database vacuum
└── administratie(id)  →  AdministratieScope
    ├── boekjaren             ← fiscal years (CRUD + open/close)
    ├── dagboeken             ← journals (CRUD + werkstatus)
    ├── grootboekrekeningen   ← chart of accounts (CRUD + balances + ledger)
    ├── btw_codes             ← VAT codes (CRUD)
    ├── auto_booking_rules    ← automatic booking rules (CRUD)
    ├── import_               ← bank statement upload
    ├── export_import         ← full export / boekjaar export / import
    ├── dagboek(id)  →  DagboekScope   (year-agnostic ops)
    │   ├── rerun_regels()
    │   ├── suggest(boeking_id)
    │   └── import_boekingen(boekingen)
    └── boekjaar(id)  →  BoekjaarScope
        ├── reports           ← balance sheet and P&L
        ├── btw_aangifte      ← quarterly VAT returns
        └── dagboek(id)  →  BoekjaarDagboekScope
            └── boekingen     ← list / create

Environment variables

Variable Description Default
MBOEK_URL Backend base URL http://localhost:3000
MBOEK_USERNAME Username for auto-login (none)
MBOEK_PASSWORD Password for auto-login (none)
# No arguments needed when env vars are set
with MboekClient() as client:
    admins = client.administraties.list()

Explicitly-passed constructor arguments always override env vars.

Authentication

# Option 1: pass credentials to the constructor (recommended)
client = MboekClient("http://localhost:3000", "admin", "geheim")

# Option 2: use environment variables (MBOEK_URL, MBOEK_USERNAME, MBOEK_PASSWORD)
client = MboekClient()

# Option 3: call login() manually
client = MboekClient("http://localhost:3000")
client.login("admin", "geheim")

# Always call logout() when done (or use the context manager)
client.logout()

Finding resources by name or code

Every list-based resource exposes find_by_naam() and/or find_by_code() as a convenience. All return None when not found.

a = client.administratie(admin_id)

# Find a fiscal year by name
boekjaar = a.boekjaren.find_by_naam("2024")

# Find a journal by name or short code (code comparison is case-insensitive)
bank = a.dagboeken.find_by_naam("Bankboek")
bank = a.dagboeken.find_by_code("bank")   # matches "BANK"

# Find a general-ledger account by name or account code
rekening = a.grootboekrekeningen.find_by_naam("Bank")
rekening = a.grootboekrekeningen.find_by_code("1220")

# Find a VAT code by its short code (case-insensitive)
btw = a.btw_codes.find_by_code("v21")    # matches "V21"

Creating a journal entry

All boekingsregels must balance (sum(bedrag) == 0). Amounts are in euros — the library converts to/from cents automatically.

from decimal import Decimal
from datetime import date
from mboek import CreateBoekingInput, CreateBoekingsregelInput, Regeltype

regels = [
    CreateBoekingsregelInput(
        grootboekrekening_id=bank_account_id,
        omschrijving="Bank outflow",
        bedrag=Decimal("-121.00"),   # credit the bank account
    ),
    CreateBoekingsregelInput(
        grootboekrekening_id=kosten_id,
        omschrijving="Hosting",
        bedrag=Decimal("100.00"),    # debit costs (netto)
        btw_code_id=btw_i21_id,
        regeltype=Regeltype.NETTO,
    ),
    CreateBoekingsregelInput(
        grootboekrekening_id=btw_vorderen_id,
        omschrijving="BTW",
        bedrag=Decimal("21.00"),     # debit VAT receivable
        regeltype=Regeltype.BTW,
        netto_ref=1,                 # index of the netto regel above
    ),
]

bj_dagboek = client.administratie(admin_id).boekjaar(boekjaar_id).dagboek(bank_dagboek_id)
entry = bj_dagboek.boekingen.create(
    CreateBoekingInput(
        datum=date(2024, 3, 15),
        omschrijving="Hosting invoice March",
        regels=regels,
        # boekjaar_id is injected automatically from the scope
    )
)
print(f"Created boeking {entry.boeking.id}")

Setting up a new administration

from datetime import date
from mboek import CreateAdministratieInput, CreateBoekjaarInput

# 1. Create the administration
admin = client.administraties.create(
    CreateAdministratieInput(naam="My Company BV", btw_nummer="NL123456789B01")
)
a = client.administratie(admin.id)

# 2. Seed the standard Dutch chart of accounts
a.grootboekrekeningen.seed_rgs()

# 3. Seed the standard Dutch BTW (VAT) codes
a.btw_codes.seed_defaults()

# 4. Create a fiscal year
boekjaar = a.boekjaren.create(
    CreateBoekjaarInput(
        naam="2024",
        start_datum=date(2024, 1, 1),
        eind_datum=date(2024, 12, 31),
    )
)

# 5. Set it as the current/active year
a.boekjaren.set_huidig(boekjaar.id)

BTW-aangifte (VAT return) workflow

bj = client.administratie(admin_id).boekjaar(boekjaar_id)

# 1. Calculate the Q1 VAT return (creates a concept)
aangifte = bj.btw_aangifte.berekenen(kwartaal=1)
print(f"Q1 VAT: {aangifte.r5g}")   # positive = te betalen, negative = te ontvangen

# 2. Close the fiscal year (required before vastleggen)
client.administratie(admin_id).boekjaren.afsluiten(boekjaar_id)

# 3. Lock the aangifte and create the balancing boeking
definitief = bj.btw_aangifte.vastleggen(aangifte.id)

Bank statement import

from pathlib import Path

result = client.administratie(admin_id).import_.upload(Path("afschrift-jan.940"))
print(f"Imported {result.imported} transactions, skipped {result.skipped} duplicates")

Export and import

import json

a = client.administratie(admin_id)

# Full export to a JSON file
payload = a.export_import.export_administratie()
with open("backup.json", "w") as f:
    json.dump(payload, f, indent=2)

# Export a single boekjaar
bj_payload = a.export_import.export_boekjaar(boekjaar_id)

# Restore a full backup into a new administration
with open("backup.json") as f:
    payload = json.load(f)
client.export_import.import_administratie(payload)

Automatic booking rules

from mboek import CreateAutoBookingRuleInput

a = client.administratie(admin_id)

rule = a.auto_booking_rules.create(
    CreateAutoBookingRuleInput(
        naam="Hetzner hosting",
        actie_type="enkel",
        tegenpartij_iban_patroon="DE75512308000000060004",
        lines=[...],
    )
)

# Re-apply all rules to unprocessed entries in a dagboek (year-agnostic)
updated = a.dagboek(bank_dagboek_id).rerun_regels()
print(f"Auto-booked {len(updated)} entries")

Reports

bj = client.administratie(admin_id).boekjaar(boekjaar_id)

# Balance sheet
balans = bj.reports.balans()
print(f"Activa: {balans.totaal_activa}  Passiva: {balans.totaal_passiva}")
print(f"In balans: {balans.in_balans}")

# Profit & loss
wv = bj.reports.winst_verlies()
print(f"Netto resultaat: {wv.netto_resultaat}")

Error handling

from mboek import (
    AuthError,         # 401 Unauthorized
    ForbiddenError,    # 403 Forbidden
    NotFoundError,     # 404 Not Found
    ConflictError,     # 409 Conflict
    ValidationError,   # 422 Unprocessable Entity
    RateLimitError,    # 429 Too Many Requests
    MboekError,        # base for all API errors
)

try:
    client.administratie(admin_id).boekjaren.afsluiten(boekjaar_id)
except ConflictError as e:
    print(f"Cannot close: {e}")   # e.g. already closed
except NotFoundError:
    print("Boekjaar not found")

All exceptions expose:

  • e.status_code — HTTP status code
  • e.detail — parsed response body (dict or str)

Dutch accounting glossary

Dutch term English equivalent
Administratie Company administration / set of books
Boekjaar Fiscal / financial year
Dagboek Journal / sub-ledger
Grootboekrekening General-ledger account
Boeking Journal entry
Boekingsregel Journal entry line
BTW VAT (value-added tax)
BTW-aangifte VAT return (quarterly)
Netto bedrag Net amount (excluding VAT)
Te betalen BTW Output VAT (VAT payable to the tax office)
Te vorderen BTW Input VAT (VAT reclaimable)
Balans Balance sheet
Winst & verlies Profit & loss statement
Activa Assets
Passiva Liabilities + equity
Kosten Costs / expenses
Opbrengsten Revenues
Debet Debit
Credit Credit
Saldo Balance
Stuknummer Document / invoice reference number
Tegenpartij Counterparty
Banktransacties Bank transactions
Automatische boekregels Automatic booking rules

Development

git clone git@github.com:newinnovations/mboek-python-client.git
cd mboek-python-client
uv venv .venv && source .venv/bin/activate
uv pip install -e ".[dev]"
pytest

Quick start

from mboek import MboekClient

# Auto-login, auto-logout via context manager
with MboekClient("http://localhost:3000", "admin", "geheim") as client:

    # List all company administrations
    admins = client.administraties.list()
    admin = admins[0]
    print(f"Administration: {admin.naam}  (id={admin.id})")

    # List fiscal years
    years = client.boekjaren.list(admin.id)
    boekjaar = next(y for y in years if y.status.value == "open")
    print(f"Open boekjaar: {boekjaar.naam}  ({boekjaar.start_datum}{boekjaar.eind_datum})")

    # List journals
    dagboeken = client.dagboeken.list(admin.id)
    bank = next(d for d in dagboeken if d.dagboek_type.value == "bank")

    # List journal entries
    boekingen = client.boekingen.list(dagboek_id=bank.id, boekjaar_id=boekjaar.id)
    for entry in boekingen[:5]:
        print(f"  {entry.boeking.datum}  {entry.boeking.omschrijving}")

    # Generate a balance sheet
    balans = client.reports.balans(admin.id, boekjaar.id)
    print(f"Activa: {balans.totaal_activa}  Passiva: {balans.totaal_passiva}  In balans: {balans.in_balans}")

Environment variables

You can configure the client entirely through environment variables — useful for scripts, CI, and twelve-factor apps:

Variable Description Default
MBOEK_URL Backend base URL http://localhost:3000
MBOEK_USERNAME Username for auto-login (none)
MBOEK_PASSWORD Password for auto-login (none)
export MBOEK_URL=http://mboek.example.com
export MBOEK_USERNAME=admin
export MBOEK_PASSWORD=geheim
# No arguments needed — reads from environment
with MboekClient() as client:
    admins = client.administraties.list()

Explicitly-passed constructor arguments always override the environment variables.

Authentication

All endpoints except /api/auth/login require a JWT bearer token. The client manages the token transparently:

# Option 1: pass credentials to the constructor (recommended)
client = MboekClient("http://localhost:3000", "admin", "geheim")

# Option 2: use environment variables (MBOEK_URL, MBOEK_USERNAME, MBOEK_PASSWORD)
client = MboekClient()

# Option 3: call login() manually
client = MboekClient("http://localhost:3000")
client.login("admin", "geheim")

# Always call logout() when done (or use the context manager)
client.logout()

Finding resources by name or code

Every list-based resource exposes find_by_naam() and/or find_by_code() as a convenience over list() + filtering yourself. All return None when not found.

# Find an administration by name
admin = client.administraties.find_by_naam("My Company BV")

# Find a fiscal year by name
boekjaar = client.boekjaren.find_by_naam(admin.id, "2024")

# Find a journal by name or short code (code comparison is case-insensitive)
bank = client.dagboeken.find_by_naam(admin.id, "Bankboek")
bank = client.dagboeken.find_by_code(admin.id, "bank")   # matches "BANK"

# Find a general-ledger account by name or account code
rekening = client.grootboekrekeningen.find_by_naam(admin.id, "Bank")
rekening = client.grootboekrekeningen.find_by_code(admin.id, "1220")

# Find a VAT code by its short code (case-insensitive)
btw = client.btw_codes.find_by_code(admin.id, "v21")     # matches "V21"

Creating a journal entry

All boekingsregels must balance (sum(bedrag) == 0). Amounts are in euros (the library converts to/from cents automatically).

from decimal import Decimal
from datetime import date
from mboek import CreateBoekingInput, CreateBoekingsregelInput

regels = [
    CreateBoekingsregelInput(
        grootboekrekening_id=bank_account_id,
        omschrijving="Bank outflow",
        bedrag=Decimal("-121.00"),   # credit the bank account
    ),
    CreateBoekingsregelInput(
        grootboekrekening_id=kosten_id,
        omschrijving="Hosting",
        bedrag=Decimal("100.00"),    # debit costs (netto)
        btw_code_id=btw_i21_id,
        regeltype=Regeltype.NETTO,
    ),
    CreateBoekingsregelInput(
        grootboekrekening_id=btw_vorderen_id,
        omschrijving="BTW",
        bedrag=Decimal("21.00"),     # debit VAT receivable
        regeltype=Regeltype.BTW,
        netto_ref=1,                 # index of the netto regel above
    ),
]

entry = client.boekingen.create(
    dagboek_id=bank_dagboek_id,
    input=CreateBoekingInput(
        datum=date(2024, 3, 15),
        omschrijving="Hosting invoice March",
        boekjaar_id=boekjaar_id,
        regels=regels,
    ),
)
print(f"Created boeking {entry.boeking.id}")

Setting up a new administration

from datetime import date
from mboek import CreateAdministratieInput, CreateBoekjaarInput

# 1. Create the administration
admin = client.administraties.create(
    CreateAdministratieInput(naam="My Company BV", btw_nummer="NL123456789B01")
)

# 2. Seed the standard Dutch chart of accounts
client.grootboekrekeningen.seed_rgs(admin.id)

# 3. Seed the standard Dutch BTW (VAT) codes
client.btw_codes.seed_defaults(admin.id)

# 4. Create a fiscal year
boekjaar = client.boekjaren.create(
    admin.id,
    CreateBoekjaarInput(
        naam="2024",
        start_datum=date(2024, 1, 1),
        eind_datum=date(2024, 12, 31),
    ),
)

# 5. Set it as the current/active year
client.boekjaren.set_huidig(admin.id, boekjaar.id)

BTW-aangifte (VAT return) workflow

# 1. Calculate the Q1 VAT return (creates a concept)
aangifte = client.btw_aangifte.berekenen(admin.id, boekjaar_id=boekjaar.id, kwartaal=1)
print(f"Q1 VAT: {aangifte.r5g}")   # positive = te betalen, negative = te ontvangen

# 2. Close the fiscal year (required before vastleggen)
client.boekjaren.afsluiten(admin.id, boekjaar.id)

# 3. Lock the aangifte and create the balancing boeking
definitief = client.btw_aangifte.vastleggen(admin.id, aangifte.id)

Bank statement import

from pathlib import Path

result = client.import_.upload(admin.id, Path("afschrift-jan.940"))
print(f"Imported {result.imported} transactions, skipped {result.skipped} duplicates")

Export and import

# Full export to a JSON file
import json
payload = client.export_import.export_administratie(admin.id)
with open("backup.json", "w") as f:
    json.dump(payload, f, indent=2)

# Restore from backup
with open("backup.json") as f:
    payload = json.load(f)
client.export_import.import_administratie(payload)

Automatic booking rules

from mboek import CreateAutoBookingRuleInput, CreateAutoBookingRuleLineInput
from mboek.models._enums import AutoBookingActieType, AutoBookingBedragType

rule = client.auto_booking_rules.create(
    admin.id,
    CreateAutoBookingRuleInput(
        naam="Hetzner hosting",
        actie_type=AutoBookingActieType.ENKEL,
        tegenpartij_iban_patroon="DE75512308000000060004",
        lines=[
            CreateAutoBookingRuleLineInput(
                grootboekrekening_id=hosting_rekening_id,
                btw_code_id=btw_i21_id,
                bedrag_type=AutoBookingBedragType.REST,
            )
        ],
    ),
)

# Re-apply all rules to unprocessed entries in a dagboek
updated = client.auto_booking_rules.rerun(admin.id, bank_dagboek_id)
print(f"Auto-booked {len(updated)} entries")

API reference

Resources

Property Description
client.administraties Company administrations (CRUD)
client.boekjaren Fiscal years (CRUD + open/close/reopen)
client.dagboeken Journals / sub-ledgers (CRUD + work status)
client.grootboekrekeningen Chart of accounts (CRUD + seed RGS + balances + ledger)
client.boekingen Journal entries (CRUD)
client.btw_codes VAT codes (CRUD + seed defaults)
client.btw_aangifte Quarterly VAT returns (calculate + lock + delete)
client.auto_booking_rules Automatic booking rules (CRUD + re-run)
client.reports Balance sheet and P&L reports
client.import_ Bank statement upload (MT940 / CAMT.053)
client.export_import Full export / import
client.maintenance Database vacuum

Error handling

from mboek import (
    AuthError,         # 401 Unauthorized
    ForbiddenError,    # 403 Forbidden
    NotFoundError,     # 404 Not Found
    ConflictError,     # 409 Conflict
    ValidationError,   # 422 Unprocessable Entity
    RateLimitError,    # 429 Too Many Requests
    MboekError,        # base for all API errors
)

try:
    client.boekjaren.afsluiten(admin_id, boekjaar_id)
except ConflictError as e:
    print(f"Cannot close: {e}")  # e.g. already closed
except NotFoundError:
    print("Boekjaar not found")

All exceptions expose:

  • e.status_code — HTTP status code
  • e.detail — parsed response body (dict or str)

Dutch accounting glossary

Dutch term English equivalent
Administratie Company administration / set of books
Boekjaar Fiscal / financial year
Dagboek Journal / sub-ledger
Grootboekrekening General-ledger account
Boeking Journal entry
Boekingsregel Journal entry line
BTW VAT (value-added tax)
BTW-aangifte VAT return (quarterly)
Netto bedrag Net amount (excluding VAT)
Te betalen BTW Output VAT (VAT payable to the tax office)
Te vorderen BTW Input VAT (VAT reclaimable)
Balans Balance sheet
Winst & verlies Profit & loss statement
Activa Assets
Passiva Liabilities + equity
Kosten Costs / expenses
Opbrengsten Revenues
Debet Debit
Credit Credit
Saldo Balance
Stuknummer Document / invoice reference number
Tegenpartij Counterparty
Banktransacties Bank transactions
Automatische boekregels Automatic booking rules

Development

cd python
uv venv .venv && source .venv/bin/activate
uv pip install -e ".[dev]"
pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mboek-0.1.0.tar.gz (74.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mboek-0.1.0-py3-none-any.whl (61.0 kB view details)

Uploaded Python 3

File details

Details for the file mboek-0.1.0.tar.gz.

File metadata

  • Download URL: mboek-0.1.0.tar.gz
  • Upload date:
  • Size: 74.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mboek-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ff2ecd16fc84a9855d8954444aa51395e53af6a4cfe90b215a4f8eb2b2e6d985
MD5 ba8ad2d1aa5d9ff75c49fedcf605d6a6
BLAKE2b-256 7f0314e4de2451396e62407e0758fd4f70b5a17b2c6b9e0e0c05f77eff471d65

See more details on using hashes here.

Provenance

The following attestation bundles were made for mboek-0.1.0.tar.gz:

Publisher: publish.yml on newinnovations/mboek-python-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mboek-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mboek-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 61.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mboek-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 85d174aece577f910ec9ca02d8b84551558f66ce9efc318a30036f45c658ac22
MD5 9327bcf9765a5122ad6e58e6c48fb702
BLAKE2b-256 852d6e6e8ba387bcce40afdb171e80ec358cfa9400c765d89c0dd84f27cf8c12

See more details on using hashes here.

Provenance

The following attestation bundles were made for mboek-0.1.0-py3-none-any.whl:

Publisher: publish.yml on newinnovations/mboek-python-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page