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 codee.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 codee.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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff2ecd16fc84a9855d8954444aa51395e53af6a4cfe90b215a4f8eb2b2e6d985
|
|
| MD5 |
ba8ad2d1aa5d9ff75c49fedcf605d6a6
|
|
| BLAKE2b-256 |
7f0314e4de2451396e62407e0758fd4f70b5a17b2c6b9e0e0c05f77eff471d65
|
Provenance
The following attestation bundles were made for mboek-0.1.0.tar.gz:
Publisher:
publish.yml on newinnovations/mboek-python-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mboek-0.1.0.tar.gz -
Subject digest:
ff2ecd16fc84a9855d8954444aa51395e53af6a4cfe90b215a4f8eb2b2e6d985 - Sigstore transparency entry: 1393507416
- Sigstore integration time:
-
Permalink:
newinnovations/mboek-python-client@8d79ace520dd753949cc9db0b1d2eb8a85939050 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/newinnovations
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8d79ace520dd753949cc9db0b1d2eb8a85939050 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85d174aece577f910ec9ca02d8b84551558f66ce9efc318a30036f45c658ac22
|
|
| MD5 |
9327bcf9765a5122ad6e58e6c48fb702
|
|
| BLAKE2b-256 |
852d6e6e8ba387bcce40afdb171e80ec358cfa9400c765d89c0dd84f27cf8c12
|
Provenance
The following attestation bundles were made for mboek-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on newinnovations/mboek-python-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mboek-0.1.0-py3-none-any.whl -
Subject digest:
85d174aece577f910ec9ca02d8b84551558f66ce9efc318a30036f45c658ac22 - Sigstore transparency entry: 1393507424
- Sigstore integration time:
-
Permalink:
newinnovations/mboek-python-client@8d79ace520dd753949cc9db0b1d2eb8a85939050 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/newinnovations
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8d79ace520dd753949cc9db0b1d2eb8a85939050 -
Trigger Event:
release
-
Statement type: