Dalux Build API – Python Client
A lightweight Python client for the Dalux Build REST API.
[!IMPORTANT] This is an unofficial client, not affiliated with or endorsed by Dalux ApS. It provides no API access or credentials. You must use your own authorized Dalux subscription and API key within your agreement with Dalux. The MIT license covers this client code only; it does not grant rights to Dalux's API or Services. Commercial or hosted use requires authorization under your own Dalux agreement. See the Legal and Usage Notice.
See the Node.js client (dalux-build-api) and the
webhook server built on this package — the two
clients are versioned, tested, and released together; see
../CONTRIBUTING.md. For running the scheduled outbound
webhook monitor embedded in a script via dalux.webhook_server, see
docs/webhook_server.md.
Requirements
- Python 3.10 or later
- requests ≥ 2.28
Installation
pip install dalux-build
Getting Started
from dalux_build import create_client
dalux = create_client(
base_url="https://<your-company>.dalux.com/api",
api_key="YOUR_API_KEY",
project_id="my-project-id", # optional: see "Client-level defaults" below
)
The returned DaluxClient object exposes one attribute per API resource group
(see API Reference below).
Examples
List all projects
projects = dalux.projects.list_projects()
print(projects) # list[Project] — pass full_response=True for the full ProjectsListResponse
Get a specific project
project = dalux.projects.get_project(project_id="my-project-id")
print(project)
List tasks on a project
tasks = dalux.tasks.get_project_tasks(
params={"updatedAfter": "2024-01-01"},
project_id="my-project-id",
)
print(tasks)
Local dashboards
Install the optional Streamlit and Plotly dependencies:
pip install "dalux-build[dashboard]"
Every API namespace exposes the same resource-scoped dashboard() method. The
first built-in template is the task lifecycle timeline:
dashboard = dalux.tasks.dashboard(
template="task-timeline",
template_options={
"timezone": "Europe/Copenhagen",
"task_params": {"typeId": "my-task-type-id"},
},
)
print(dashboard.url)
dashboard.stop()
The call starts a local Streamlit process, opens it in the default browser, and
returns a DashboardHandle. Pass open_browser=False to start without opening
a tab, or port=8501 to select a port. The process uses the client's configured
project by default; pass template_options={"project_id": "another-project"} to
override it.
Templates are scoped to their owning API. For example,
dalux.files.dashboard(...) and dalux.folders.dashboard(...) are available
for future file and folder templates, but reject task-timeline. Inspect
dalux.files.available_dashboards to discover templates registered for that
namespace.
Upload a file (chunked)
# 1. Create an upload slot
upload = dalux.file_upload.create_upload(
{"fileName": "drawing.pdf", "mimeType": "application/pdf"},
project_id="my-project-id",
file_area_id="my-file-area-id",
)
upload_guid = upload["uploadGuid"]
# 2. Upload the file content
with open("drawing.pdf", "rb") as f:
dalux.file_upload.upload_file_part(
upload_guid, f.read(), project_id="my-project-id", file_area_id="my-file-area-id"
)
# 3. Finalize
result = dalux.file_upload.finish_upload(
upload_guid,
{"folderId": "target-folder-id"},
project_id="my-project-id",
file_area_id="my-file-area-id",
)
print("New file ID:", result["fileId"])
Client-level defaults (project_id / file_area_id)
Most methods take project_id (and, where relevant, file_area_id) as a
keyword-only argument. If you mostly work against a single project, set a
default once on the client and omit it everywhere else:
dalux = create_client(
base_url="https://<your-company>.dalux.com/api",
api_key="YOUR_API_KEY",
project_id="my-project-id", # or set DALUX_PROJECT_ID
file_area_id="my-file-area-id", # or set DALUX_FILE_AREA_ID
)
dalux.tasks.get_project_tasks() # uses the default project_id
dalux.files.get_files() # uses the default project_id + file_area_id
dalux.tasks.get_project_tasks(project_id="other-project-id") # explicit value wins
An explicit project_id/file_area_id passed to a call always overrides the
client default; a ValidationError is raised if neither is available.
full_response
Single-page list methods (list_projects, list_files, …) default to returning
just the plain list[...] of items. Pass full_response=True to get the full
response model instead, which also exposes .metadata (pagination info) and .links:
files = dalux.files.get_files(project_id="p1", file_area_id="fa1") # list[File]
# Note: get_files() paginates automatically; for single-page results, use list_files()
response = dalux.files.list_files(project_id="p1", file_area_id="fa1", full_response=True)
response.items # same list[File]
response.metadata # Metadata(total_items=..., total_remaining_items=...)
response.links # pagination links
to_dataframe
The same list/collection methods also accept to_dataframe=True, returning the
items flattened into a pandas DataFrame directly
— nested objects are flattened into ::-separated column names (e.g.
owner::userId). Requires pandas to be installed (pip install pandas); takes
precedence over full_response if both are passed.
df = dalux.tasks.get_project_tasks(project_id="p1", to_dataframe=True)
df.columns # e.g. Index(['taskId', 'title', 'type::typeId', 'type::name', ...])
# Equivalent to, but shorter than:
response = dalux.tasks.get_project_tasks(project_id="p1", full_response=True)
df = response.to_dataframe() if response else pd.DataFrame()
The primary paginated methods (get_files, get_folders,
get_project_tasks, list_inspection_plans, …) automatically follow
pagination and accept to_dataframe=True too — they have no full_response mode
(they return a bare list already), but flatten the same way:
df = dalux.files.get_files(project_id="p1", file_area_id="fa1", to_dataframe=True)
Authentication
Every request automatically includes the X-API-KEY header with the API key
supplied to create_client. No additional configuration is required.
API keys are managed in the Dalux Build UI under Settings › Integrations › API Identities. Contact support@dalux.com to activate API access for your company profile.
Error Handling
All methods raise requests.HTTPError on 4xx / 5xx responses:
import requests
try:
project = dalux.projects.get_project(project_id="unknown-id")
except requests.HTTPError as exc:
print(exc.response.status_code, exc.response.json())
API Reference
| Attribute | Class | Description |
|---|---|---|
projects |
ProjectsApi |
List, get, create and update projects; project metadata |
companies |
CompaniesApi |
Project companies (CRUD) |
company_catalog |
CompanyCatalogApi |
Company catalog (CRUD + metadata) |
tasks |
TasksApi |
Tasks, approvals, safety issues, observations & good practices |
file_areas |
FileAreasApi |
File areas on a project |
files |
FilesApi |
Files within a file area |
folders |
FoldersApi |
Folders within a file area |
file_upload |
FileUploadApi |
Chunked upload (create → part → finalize) |
file_revisions |
FileRevisionsApi |
Download file revision content |
forms |
FormsApi |
Forms and form attachments |
users |
UsersApi |
Company and project users |
project_templates |
ProjectTemplatesApi |
Available project templates |
inspection_plans |
InspectionPlansApi |
Inspection plans, items, zones, registrations |
test_plans |
TestPlansApi |
Test plans, items, zones, registrations |
version_sets |
VersionSetsApi |
Version sets and version set files |
work_packages |
WorkPackagesApi |
Work packages on a project |
project_id and file_area_id are keyword-only in every method below (e.g.
get_task(task_id, *, project_id=None)) and fall back to the client's
configured default when omitted — see
Client-level defaults. List
methods additionally accept full_response=False (see
full_response) and to_dataframe=False (see
to_dataframe) — omitted from the signatures below for
brevity.
ProjectsApi
| Method | HTTP | Path |
|---|---|---|
list_projects(params=None, full_response=False) |
GET | /5.1/projects |
get_project(*, project_id=None) |
GET | /5.0/projects/{projectId} |
create_project(body) |
POST | /5.0/projects |
update_project(body, *, project_id=None) |
PATCH | /5.0/projects/{projectId} |
list_metadata_mappings_for_projects() |
GET | /1.0/projects/metadata/1.0/mappings |
list_metadata_values_for_projects(key) |
GET | /1.0/projects/metadata/1.0/mappings/{key}/values |
list_project_metadata(*, project_id=None) |
GET | /1.0/projects/{projectId}/metadata |
list_project_metadata_mappings(*, project_id=None) |
GET | /1.0/projects/{projectId}/metadata/1.0/mappings |
list_project_metadata_values(key, *, project_id=None) |
GET | /1.0/projects/{projectId}/metadata/1.0/mappings/{key}/values |
CompaniesApi
| Method | HTTP | Path |
|---|---|---|
list_project_companies(params=None, full_response=False, *, project_id=None) |
GET | /3.1/projects/{projectId}/companies |
get_project_company(company_id, *, project_id=None) |
GET | /3.0/projects/{projectId}/companies/{companyId} |
create_project_company(body, *, project_id=None) |
POST | /3.1/projects/{projectId}/companies |
update_project_company(company_id, body, *, project_id=None) |
PATCH | /3.0/projects/{projectId}/companies/{companyId} |
CompanyCatalogApi
Account-level (not project-scoped) — no project_id.
| Method | HTTP | Path |
|---|---|---|
get_companies(params=None, full_response=False) |
GET | /2.2/companyCatalog |
get_company(catalog_company_id) |
GET | /1.2/companyCatalog/{catalogCompanyId} |
create_company(body) |
POST | /2.2/companyCatalog |
update_company(catalog_company_id, body) |
PATCH | /2.1/companyCatalog/{catalogCompanyId} |
list_company_metadata(catalog_company_id) |
GET | /1.0/companyCatalog/{catalogCompanyId}/metadata |
list_company_metadata_mappings(catalog_company_id) |
GET | /1.0/companyCatalog/{catalogCompanyId}/metadata/1.0/mappings |
list_company_metadata_values(catalog_company_id, key) |
GET | /1.0/.../metadata/1.0/mappings/{key}/values |
list_metadata_mappings_for_companies() |
GET | /1.0/companyCatalog/metadata/1.0/mappings |
list_metadata_values_for_companies(key) |
GET | /1.0/companyCatalog/metadata/1.0/mappings/{key}/values |
TasksApi
| Method | HTTP | Path |
|---|---|---|
get_project_tasks(params=None, verbose=False, *, project_id=None) |
GET (paginated) | /5.1/projects/{projectId}/tasks |
get_task(task_id, *, project_id=None) |
GET | /3.3/projects/{projectId}/tasks/{taskId} |
get_project_task_changes(params=None, verbose=False, *, project_id=None) |
GET (paginated) | /2.2/projects/{projectId}/tasks/changes |
get_project_task_attachments(params=None, full_response=False, *, project_id=None) |
GET | /1.1/projects/{projectId}/tasks/attachments |
FileAreasApi
| Method | HTTP | Path |
|---|---|---|
get_file_areas(params=None, full_response=False, *, project_id=None) |
GET | /5.1/projects/{projectId}/file_areas |
get_file_area(*, project_id=None, file_area_id=None) |
GET | /1.0/projects/{projectId}/file_areas/{fileAreaId} |
FilesApi
Browse (get_files, get_files_in_folder, …) uses GET
/6.1/projects/{projectId}/file_areas/{fileAreaId}/files with automatic pagination.
Single-page access via list_files. get_file uses 5.0 for a single file id (Dalux Build API 4.14).
| Method | HTTP | Path |
|---|---|---|
list_files(params=None, full_response=False, *, project_id=None, file_area_id=None) |
GET | /6.1/projects/{projectId}/file_areas/{fileAreaId}/files |
get_files / get_files_in_folder / bulk helpers |
GET (paginated) | Same 6.1 browse path (pagination in the client) |
get_file(file_id=None, ..., *, path=None, project_id=None, file_area_id=None) |
GET | /5.0/projects/{projectId}/file_areas/{fileAreaId}/files/{fileId} |
get_file_properties_mapping(file_id, *, project_id=None, file_area_id=None) |
GET | /1.0/.../files/{fileId}/properties/1.0/mappings |
get_file_property_mapping_values(file_property_id, *, project_id=None, file_area_id=None) |
GET | /1.0/.../files/properties/1.0/mappings/{filePropertyId}/values |
bulk_download_files' own file_area_id parameter is the exception: passing
None there selects path-based resolution and is intentionally not
backfilled from the client default.
FoldersApi
| Method | HTTP | Path |
|---|---|---|
list_folders(params=None, full_response=False, *, project_id=None, file_area_id=None) |
GET | /5.1/.../folders |
get_folders(params=None, verbose=False, *, project_id=None, file_area_id=None) |
GET (paginated) | /5.1/.../folders |
get_folder(folder_id, *, project_id=None, file_area_id=None) |
GET | /5.0/.../folders/{folderId} |
get_folder_files_properties(folder_id, *, project_id=None, file_area_id=None) |
GET | /1.0/.../folders/{folderId}/files/properties/1.0/mappings |
FileUploadApi
| Method | HTTP | Path |
|---|---|---|
create_upload(body, *, project_id=None, file_area_id=None) |
POST | /1.0/.../upload |
upload_file_part(upload_guid, chunk, *, project_id=None, file_area_id=None) |
POST | /1.0/.../upload/{uploadGuid} |
finish_upload(upload_guid, body, *, project_id=None, file_area_id=None) |
POST | /2.0/.../upload/{uploadGuid}/finalize |
FileRevisionsApi
| Method | HTTP | Path |
|---|---|---|
get_file_revision_content(file_id, file_revision_id, *, project_id=None, file_area_id=None) |
GET | /2.0/.../revisions/{fileRevisionId}/content |
FormsApi
| Method | HTTP | Path |
|---|---|---|
get_project_forms(params=None, full_response=False, *, project_id=None) |
GET | /2.1/projects/{projectId}/forms |
get_form(form_id, *, project_id=None) |
GET | /1.2/projects/{projectId}/forms/{formId} |
get_project_form_attachments(params=None, *, project_id=None) |
GET | /2.1/projects/{projectId}/forms/attachments |
UsersApi
| Method | HTTP | Path |
|---|---|---|
get_user(user_id) |
GET | /1.1/users/{userId} |
list_project_users(params=None, full_response=False, *, project_id=None) |
GET | /1.2/projects/{projectId}/users |
get_project_user(user_id, *, project_id=None) |
GET | /1.1/projects/{projectId}/users/{userId} |
ProjectTemplatesApi
| Method | HTTP | Path |
|---|---|---|
list_project_templates(params=None) |
GET | /1.1/projectTemplates |
InspectionPlansApi
| Method | HTTP | Path |
|---|---|---|
list_inspection_plans(params=None, full_response=False, *, project_id=None) |
GET | /1.2/projects/{projectId}/inspectionPlans |
list_inspection_plan_items(params=None, full_response=False, *, project_id=None) |
GET | /1.1/projects/{projectId}/inspectionPlanItems |
list_inspection_plan_item_zones(params=None, full_response=False, *, project_id=None) |
GET | /1.1/projects/{projectId}/inspectionPlanItemZones |
list_inspection_plan_registrations(params=None, full_response=False, *, project_id=None) |
GET | /2.1/projects/{projectId}/inspectionPlanRegistrations |
TestPlansApi
| Method | HTTP | Path |
|---|---|---|
list_test_plans(params=None, full_response=False, *, project_id=None) |
GET | /1.2/projects/{projectId}/testPlans |
list_test_plan_items(params=None, full_response=False, *, project_id=None) |
GET | /1.1/projects/{projectId}/testPlanItems |
list_test_plan_item_zones(params=None, full_response=False, *, project_id=None) |
GET | /1.1/projects/{projectId}/testPlanItemZones |
list_test_plan_registrations(params=None, full_response=False, *, project_id=None) |
GET | /1.1/projects/{projectId}/testPlanRegistrations |
VersionSetsApi
| Method | HTTP | Path |
|---|---|---|
get_version_sets(params=None, full_response=False, *, project_id=None) |
GET | /2.1/projects/{projectId}/version_sets |
get_version_set(version_set_id, *, project_id=None) |
GET | /2.0/projects/{projectId}/version_sets/{versionSetId} |
list_file_area_version_sets(params=None, full_response=False, *, project_id=None, file_area_id=None) |
GET | /2.1/.../file_areas/{fileAreaId}/version_sets |
list_version_set_files(version_set_id, params=None, full_response=False, *, project_id=None) |
GET | /3.0/.../version_sets/{versionSetId}/files |
WorkPackagesApi
| Method | HTTP | Path |
|---|---|---|
list_work_packages(params=None, full_response=False, *, project_id=None) |
GET | /1.0/projects/{projectId}/workpackages |
Advanced Usage
Using individual API classes directly
from dalux_build.configuration import Configuration
from dalux_build.api_client import ApiClient
from dalux_build.api import ProjectsApi, TasksApi
config = Configuration(
base_url="https://<company>.dalux.com/api",
api_key="YOUR_API_KEY",
)
api_client = ApiClient(config)
projects = ProjectsApi(api_client)
tasks = TasksApi(api_client)
Testing
cd python
pip install -e ".[dev,webhook]"
pytest --cov=dalux_build --cov-report=term-missing
CI runs this on Python 3.11 and 3.13, plus the
webhook server's own tests against this checkout's
editable install (not the published PyPI package) — see
../.github/workflows/tests.yml.
Releasing
This package is versioned and published together with the Node.js client by
Changesets — there is no manual edit
of version in pyproject.toml, and nothing publishes to PyPI unless the full
test suite (Node.js, Python, webhook server) passes first. See
../CONTRIBUTING.md for the full flow and
../README.md for the npm side of it.
License
MIT
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 dalux_build-2.4.0.tar.gz.
File metadata
- Download URL: dalux_build-2.4.0.tar.gz
- Upload date:
- Size: 157.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
101ee95df54602f72d5c009d3577646705700f75af25029d46bb2ae79219d4b9
|
|
| MD5 |
ebe0c19253ee4490ff22f601af56dbbc
|
|
| BLAKE2b-256 |
728de741a18437b4ccf6e5101b0d13f0305fcb2c6bd2504a14d637c9a39c3e35
|
File details
Details for the file dalux_build-2.4.0-py3-none-any.whl.
File metadata
- Download URL: dalux_build-2.4.0-py3-none-any.whl
- Upload date:
- Size: 174.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dddfb4203e06113481e77190292479f206b6e0036f40019f085a65cbfe5fc213
|
|
| MD5 |
5ae2fd58d208c9a5aa0e5e2ccd010c75
|
|
| BLAKE2b-256 |
4c4553bb342f4c16c316542197050cf806d79a458e2d4a87c1fae0a5cabce1cc
|