Scoped Contacts

Read and manage contact books at organization, cluster, and location scope.

Contacts can belong to an organization, cluster, or location. The application capability and the containing resource's visibility are independent: access to a cluster does not automatically expose its contact book, and contact-book access does not reveal a hidden cluster or location.

Endpoints

Use the scoped routes to list or create contacts:

GET  /v1/orgs/{organizationId}/contacts
POST /v1/orgs/{organizationId}/contacts
GET  /v1/spatial/clusters/{clusterId}/contacts
POST /v1/spatial/clusters/{clusterId}/contacts
GET  /v1/spatial/locations/{locationId}/contacts
POST /v1/spatial/locations/{locationId}/contacts

Use the contact ID for exact-target operations:

GET    /v1/contacts/{contactId}
PUT    /v1/contacts/{contactId}
DELETE /v1/contacts/{contactId}

Authorization

Reads require the exact current credential's final, tier-capped ContactsRead=Allow. Creates, updates, and deletes independently require ContactsWrite=Allow. Neither capability implies the other.

The same principal must also have the scope's final visibility:

  • organization contacts require OrganizationView=Allow;
  • cluster contacts require ClusterView=Read for that cluster;
  • location contacts require LocationView=Read for that location, including its same-principal ClusterView gate.

Permission groups and direct assignments, the organization's exact entitlement version, and personal access token organization and capability ceilings all participate in the final decision. A visible scope with a missing contact capability returns the structured 403 Forbidden permission denial. A hidden scope uses the same metadata-free 404 Not Found response as an unknown scope.

List authorization runs before search, sorting, and offset/limit pagination, so hidden contact books do not expose matches, ordering, page boundaries, or hasNextPage changes.

Existing user-owned clusters retain only their narrow legacy compatibility: the owner and an active direct-user Contributor may use that exact cluster's contact book. Viewer rows do not grant contact access, and a personal access token cannot use this owner-without-organization compatibility path.

Contact Modes

Use mode: "Direct" to store standalone contact fields such as companyName, contactPerson, email, phone, and address on the contact. contactPerson is required.

Use mode: "User" with userId to link an existing user. Do not send direct contact fields in this mode. Responses resolve the linked user's current display name, company, email, address, and phone numbers; notes remains local to the contact entry.

{
  "type": "Architect",
  "mode": "Direct",
  "companyName": "Studio Nord",
  "contactPerson": "Anna Beispiel",
  "email": "anna@example.com",
  "notes": "Lead planner"
}

Listing

List routes accept offset, limit, search, type, sortBy, and sortDir. Supported sort fields are name, companyName, type, createdAt, and updatedAt. The response sets hasNextPage when another authorized matching row exists beyond the requested page.

Profile Pictures

User profile pictures are shared profile fields. Load supported formats and size limits with GET /v1/users/profile-picture/requirements, then upload file as multipart data to PUT /v1/users/me/profile-picture. Optional cropX, cropY, cropWidth, and cropHeight must all be supplied together and describe a square inside the oriented image. Without a crop, the server uses the largest centered square.

The server detects enabled JPEG, PNG, and WebP formats from the bytes, applies orientation before cropping, and returns a metadata-stripped PNG at the configured size. Renaming a file or changing its MIME type does not enable another format. Animated images (including APNG and animated WebP), multi-frame images, malformed files, and images beyond processing limits return HTTP 400. Delete the current picture with DELETE /v1/users/me/profile-picture.

Invalid raster data, unsupported formats, animation, and invalid crop or size limits return HTTP 400 with code profile_picture_invalid and retryable=false. A rejected upload preserves the current picture. Upload and delete return HTTP 409 with code profile_picture_write_unavailable when another profile-picture mutation is pending or account closure has begun. An uncertain storage write or delete stays blocked until it is reconciled; retrying immediately cannot override it. If deletion times out after profile metadata was cleared or a replacement was stored, the request can fail while the write remains blocked.

Personal Measurement Profile

GET /v1/users/me includes defaultMeasurementProfileId, the account's effective picker default. Supported stable IDs are roofing, roadworks, utilities, and earthworks. Accounts without a saved preference receive roofing.

Save with PATCH /v1/users/me and { "defaultMeasurementProfileId": "utilities" }. Omission preserves the existing value; explicit null resets it. Empty or unsupported IDs return HTTP 400 before any profile fields are persisted. The preference is returned only for the authenticated account's own profile, including its profile-picture upload/delete responses. Other-user/contact projections omit it, even when the caller can read that user's contact details.

The preference belongs to the user across devices. It is not a location/model setting and does not reclassify, filter or delete saved measurements. Clients may offer a temporary picker selection separately from the saved default.

On this page