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)
projects = await client.get_resource_projects(resource_id) # use .reference, not .title
project = await client.get_project(projects[0].id) # .company resolved
types = await client.get_resource_type_dictionary() # label -> id, cached
# 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")
get_resource() and search_resources_by_email() always resolve the
mainManager/hrManager relationships into full Resource objects,
including their email (one level deep, so a manager's own managers stay
None). Each manager can cost up to 2 extra GETs: one for the profile
(skipped if present in the response's included data) and one to
/resources/{id}/information, since the basic resource payload never
carries emails — up to 4 extra GETs per resource, up to 4N for a search
returning N results, though managers shared between results are only
resolved once per call. A dangling manager reference or a failed email
fetch (404) is logged and resolves to None/no email rather than failing
the whole call. That resolution runs concurrently, so the client caps
itself at 5 requests in flight at once by default — pass
max_concurrent_requests= to change it, or semaphore= to share one limit
across multiple client instances.
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.
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.2.tar.gz.
File metadata
- Download URL: boondmanager_client-0.0.2.tar.gz
- Upload date:
- Size: 251.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e26a7be6e94605ae3399a4ea3ec67e97d06af93f23a55a037bd4c9102152a9f3
|
|
| MD5 |
7bcc07238b2b689dbd957febcd220821
|
|
| BLAKE2b-256 |
3ff0b23beeaf71c4a797621839bd60cab73a037d0bb229f87d1e9a2a63a92b96
|
Provenance
The following attestation bundles were made for boondmanager_client-0.0.2.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.2.tar.gz -
Subject digest:
e26a7be6e94605ae3399a4ea3ec67e97d06af93f23a55a037bd4c9102152a9f3 - Sigstore transparency entry: 2189595969
- Sigstore integration time:
-
Permalink:
Lenstra/boondmanager-client-python@eee74b48e9dafd4c3c329ccd371548ef2bec12b4 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/Lenstra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eee74b48e9dafd4c3c329ccd371548ef2bec12b4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file boondmanager_client-0.0.2-py3-none-any.whl.
File metadata
- Download URL: boondmanager_client-0.0.2-py3-none-any.whl
- Upload date:
- Size: 216.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 |
f7350995fefd0444c5ab6d7f301fd6d215a30ff6119763a1085620a33e1c91e8
|
|
| MD5 |
9c2498be3a47b1c001c4ef11044d264c
|
|
| BLAKE2b-256 |
406cdb8ed541cbf5949d37a3fdc66989f0b58777c814ddb1365cb66aba756016
|
Provenance
The following attestation bundles were made for boondmanager_client-0.0.2-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.2-py3-none-any.whl -
Subject digest:
f7350995fefd0444c5ab6d7f301fd6d215a30ff6119763a1085620a33e1c91e8 - Sigstore transparency entry: 2189595984
- Sigstore integration time:
-
Permalink:
Lenstra/boondmanager-client-python@eee74b48e9dafd4c3c329ccd371548ef2bec12b4 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/Lenstra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eee74b48e9dafd4c3c329ccd371548ef2bec12b4 -
Trigger Event:
push
-
Statement type: