Async Python client for the BoondManager API
Project description
boondmanager
Async Python client for the BoondManager API.
Why it's built this way
BoondManager validates write payloads against strict JSON schemas
(additionalProperties: false, all keys required) and silently drops any
non-conforming item while returning HTTP 200. A malformed timesheet entry
doesn't fail; it just never persists.
To make that class of bug impossible to reintroduce, the client is driven by BoondManager's own published spec:
boondmanager/_spec/spec.json.gz— vendored bundle: the full endpoint registry (598 endpoints, 844 methods) plus every published request-body JSON schema, extracted from their public RAML build.- Every request body is validated against the matching schema before
sending; mismatches raise
BoondManagerValidationErrorwith the exact violations instead of silently losing data. update_times_reportadditionally raisesBoondManagerSilentDropErrorwhen the server returns 200 but persisted fewer entries than were sent (schema-valid but semantically rejected, e.g. a nonexistent row id).
Three API layers
from boondmanager import BoondManagerClient
async with BoondManagerClient(client_token=..., client_key=..., user_token=...) as client:
# 1. High-level domain objects — no BoondManager quirks to know about
ts = await client.fetch_timesheet("1652")
ts.row(project="23").set("2026-06-30", 0.5) # delivery auto-picked, rows managed
await ts.save() # validated, silent-drop guarded
# 2. Curated helpers returning pydantic models
report = await client.get_times_report("1652")
reports = await client.get_resource_times_reports(resource_id)
# 3. Full generated surface — one namespace per resource, every endpoint
doc = await client.api.times_reports.search(params={"period": "2026-06"})
doc = await client.api.projects.get("23")
await client.api.times_reports.validate("1652")
client.api (boondmanager/api.py, generated) covers the entire public API
as namespaces: client.api.times_reports.update(id, body),
client.api.resources.absences_reports(id), ... Its methods return
Document objects — dict-compatible JSON:API wrappers with navigation:
doc = await client.api.times_reports.get("1652")
report = doc.one # Entity: report["term"], report.rel("projects")
for project in doc.included("project"):
deliveries = doc.related(project, "deliveries")
api.ENDPOINT_INDEX maps ("PUT", "/times-reports/{id}") to
("times_reports", "update") for programmatic discovery, and
client.request(method, path, json=..., params=..., validate=True) is the
underlying escape hatch.
To find a method, grep docs/API.md — one line per endpoint
method with its required query params. Each generated method's docstring
documents all query parameters (name, type, required, enum, default) and the
request-body schema name.
Timesheet gotchas (learned the hard way)
Timesheet (layer 1) absorbs all of these; they only matter when using the
lower layers directly.
regularTimesentries:row <= -1creates a new row (entries sharing the same negative value land on the same row);row >= 1must reference an existing row id. Unknown positive ids are silently discarded.- Missing relationships are
{"data": null}, never a barenullor an omitted key. exceptionalTimeshave noduration/row, andproject/deliveryare mandatory there.- BoondManager mirrors
exceptionalTimesentries intoregularTimesfor activity totals. Always filter byactivity_type in {"exceptionalTime", "exceptionalCalendar"}before iteratingregularTimes—rows()andeditable_rows()onTimesReportalready do this. - Keys that appear in GET responses (
calendar, entryids for new items) are not accepted in PUT bodies.
Regenerating the spec and API surface
When BoondManager publishes API changes:
uv run scripts/fetch_spec.py # re-download RAML + schemas -> _spec/spec.json.gz
uv run scripts/generate_api.py # regenerate boondmanager/api.py + docs/API.md
uv run pytest # sanity: registry, validation, api surface
A handful of schemas are referenced by the RAML but not published (listed in
scripts/fetch_spec.py); those endpoints work but skip pre-send validation.
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 boondmanager_client-0.0.1.tar.gz.
File metadata
- Download URL: boondmanager_client-0.0.1.tar.gz
- Upload date:
- Size: 246.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4faf274ea3ea99f5b98c6aedd11729fe73c23b43f1f24154404dbe038b54326
|
|
| MD5 |
84e5bb787eb5da23a621199db38ead91
|
|
| BLAKE2b-256 |
ae369e3aad31d13d51c276bdad2339942a00b941c26783843cfeada4770d26f9
|
Provenance
The following attestation bundles were made for boondmanager_client-0.0.1.tar.gz:
Publisher:
publish.yml on Lenstra/boondmanager-client-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
boondmanager_client-0.0.1.tar.gz -
Subject digest:
b4faf274ea3ea99f5b98c6aedd11729fe73c23b43f1f24154404dbe038b54326 - Sigstore transparency entry: 2116710061
- Sigstore integration time:
-
Permalink:
Lenstra/boondmanager-client-python@60263227d1a8487d6eda6fff03a4406057ff924b -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/Lenstra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@60263227d1a8487d6eda6fff03a4406057ff924b -
Trigger Event:
push
-
Statement type:
File details
Details for the file boondmanager_client-0.0.1-py3-none-any.whl.
File metadata
- Download URL: boondmanager_client-0.0.1-py3-none-any.whl
- Upload date:
- Size: 211.4 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 |
53372d427cb0906aa69278715b825669788dc404b74779455956766f568957a9
|
|
| MD5 |
873019ea7a7d4b5d49c2584980169824
|
|
| BLAKE2b-256 |
30ea3c8a9ad65c9a3e6faeab073e6d07b436a9be4b6eef94afb2451a45acfff6
|
Provenance
The following attestation bundles were made for boondmanager_client-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on Lenstra/boondmanager-client-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
boondmanager_client-0.0.1-py3-none-any.whl -
Subject digest:
53372d427cb0906aa69278715b825669788dc404b74779455956766f568957a9 - Sigstore transparency entry: 2116710139
- Sigstore integration time:
-
Permalink:
Lenstra/boondmanager-client-python@60263227d1a8487d6eda6fff03a4406057ff924b -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/Lenstra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@60263227d1a8487d6eda6fff03a4406057ff924b -
Trigger Event:
push
-
Statement type: