Skip to main content

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

dalux_build-2.4.0.tar.gz (157.3 kB view details)

Uploaded Source

Built Distribution

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

dalux_build-2.4.0-py3-none-any.whl (174.3 kB view details)

Uploaded Python 3

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

Hashes for dalux_build-2.4.0.tar.gz
Algorithm Hash digest
SHA256 101ee95df54602f72d5c009d3577646705700f75af25029d46bb2ae79219d4b9
MD5 ebe0c19253ee4490ff22f601af56dbbc
BLAKE2b-256 728de741a18437b4ccf6e5101b0d13f0305fcb2c6bd2504a14d637c9a39c3e35

See more details on using hashes here.

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

Hashes for dalux_build-2.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dddfb4203e06113481e77190292479f206b6e0036f40019f085a65cbfe5fc213
MD5 5ae2fd58d208c9a5aa0e5e2ccd010c75
BLAKE2b-256 4c4553bb342f4c16c316542197050cf806d79a458e2d4a87c1fae0a5cabce1cc

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.4.0 This release

2 files

2.3.0

2 files

2.2.0

2 files

2.1.4

2 files

2.1.3

2 files

2.1.2

2 files

2.1.1

2 files

2.1.0

2 files

2.0.4

2 files

2.0.3

2 files

2.0.2

2 files

2.0.1

2 files

1.1.5

2 files

1.1.4

2 files

1.1.3

2 files

1.1.2

2 files

1.1.1

2 files

1.1.0

2 files

1.0.4

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page