Cluster Sharing and Permissions

The workflow for provisioning cluster shares and handling read and write access across spatial scopes.

Spatial clusters contain locations and their related documents, bundles, and annotations. Cluster lifecycle authority and shared read/write access are separate decisions.

Cluster Lifecycle

Create an organization-owned cluster with POST /v1/spatial/clusters. Set the JSON ownerType to the number 1 (Org), not the string "Org"; new user-owned clusters are rejected. New locations must also belong to an organization-owned cluster. Creation requires the current credential's final ClusterCreate=Allow decision in that organization. An organization role or membership does not independently authorize creation, and the final decision remains subject to the exact organization entitlement version and any personal-access-token organization and capability ceilings.

Use PATCH /v1/spatial/clusters/{clusterId} to change cluster metadata and DELETE /v1/spatial/clusters/{clusterId} to remove a cluster and its documents. Update requires final ClusterUpdate=Allow, while delete independently requires final ClusterDelete=Allow. Being allowed to update never implies delete authority.

Known-resource lifecycle authorization failures return the stable structured 403 Forbidden permission-denial payload. Its categories distinguish an ordinary missing capability from an organization entitlement ceiling, a PAT ceiling, and any applicable operational prerequisite. A missing cluster returns 404 Not Found.

List Visibility

GET /v1/spatial/clusters requires final ClusterView=Read for every returned cluster. The API evaluates group and direct assignments, protected-owner and legacy Contributor sources, the exact organization entitlement version, and the current credential ceiling. It removes unauthorized clusters before search, sorting, and offset/limit pagination. Hidden matches therefore do not change the returned page or hasNextPage.

Supplying ownerType and ownerId only narrows the authorized result; owner membership by itself does not make a cluster visible. Personal access tokens are also restricted to their current organization bindings and capability maximums.

GET /v1/spatial/clusters/{clusterId} applies the same final ClusterView=Read decision to cluster detail. An active, verified direct-user Contributor compatibility row can still supply that cluster-local read source, but it does not grant ClusterUpdate or ClusterDelete.

Discover Shared Clusters Across Organizations

Use GET /v1/spatial/shared-clusters for a current-user picker that contains only clusters marked as shared. The request does not need an active organization or cluster selection. It unions active shared-resource workflows with still-unmigrated direct-user Contributor compatibility rows, deduplicates the same cluster across those sources, and runs the current evaluator before offset/limit pagination. Revoked provenance, stale grants, and candidates that no longer have final ClusterView=Read are omitted without shortening the authorized page.

Each item contains safe organization and cluster display data, including the cluster color fields, plus a provenance summary. isSharedAccessGuest means that the active shared-resource workflow originally created the user's zero-permission membership shell; it is presentation metadata and never grants access. hasLegacyContributorCompatibility identifies a still-active legacy candidate. The effectiveCapabilities collection reports the current final value, entitlement-blocked state, and allowed actions for every capability evaluated in the cluster context. These values already include the owning organization's exact entitlement version and any PAT organization/capability ceilings.

GET /v1/spatial/shared-clusters?offset=0&limit=100

Resolve a share target

Before creating a share, search the central privacy-aware user directory:

GET /v1/users?search=person&limit=20&includeProfilePicture=true

Three or more trimmed characters search every active, non-rejected account. Accounts known through an organization roster the caller may view can match and return their authorized profile/contact fields. External accounts match only by username and return only id, username, isKnown: false, and an optional temporary profilePicture; all other profile fields are blank or null.

Use the selected result's id as targetUserId in the shared-Cluster create request. Cluster and Document-folder pickers use this same directory. Creation still revalidates exact resource authority, invitation/membership, delegation, entitlement, concurrency, and target state.

The former GET /v1/spatial/shared-clusters/targets?organizationId=...&clusterId=... route is deprecated and remains temporarily available for client compatibility.

Create and manage authenticated shared access

Use POST /v1/spatial/shared-clusters to share one organization-owned Cluster with an existing active user. Supply a stable client-generated workflowId; that ID is the idempotency identity for an ambiguous retry. The request also contains the owning organizationId, clusterId, targetUserId, and the exact ordinary permission contributions selected by the sharing interface:

{
  "workflowId": "11111111-1111-4111-8111-111111111111",
  "organizationId": "22222222-2222-4222-8222-222222222222",
  "clusterId": "33333333-3333-4333-8333-333333333333",
  "targetUserId": "44444444-4444-4444-8444-444444444444",
  "grants": [
    {
      "capabilityKey": "ClusterView",
      "scopeType": "Cluster",
      "scopeId": "33333333-3333-4333-8333-333333333333",
      "parentClusterId": "33333333-3333-4333-8333-333333333333",
      "value": "Read"
    }
  ]
}

Every workflow requires exact ClusterView=Read for the shared Cluster. Other selected grants must be ordinary delegable resource permissions inside that Cluster subtree. Administrative permissions, exact-target cleanup actions, unrelated resources, entitlement versions, product grants, storage overrides, and Cluster/Location/Job capacity changes are rejected.

The actor must be able to manage and delegate every requested contribution. When the target has no organization membership, the actor also needs OrganizationMembersInvite; PixService creates a zero-permission Viewer-valued compatibility shell and records that this workflow owns its membership purpose. An existing membership and its role are preserved. In both cases, the target's final access remains capped by the owning organization's current entitlement version, PAT ceiling, and organization-wide operational limits.

An exact retry with the same workflow ID and contributions returns the current workflow without incrementing the permission version or creating another membership, assignment, audit event, or outbox event. Reusing the workflow ID or the same organization/user/Cluster identity with different create content returns a conflict instead of silently changing access.

Use PUT /v1/spatial/shared-clusters/{workflowId} to replace the set of contributions owned by that workflow. Include expectedAccessConcurrencyVersion and the complete desired grants collection. Removed workflow origins are revoked, but a canonical assignment remains when a manual, invitation, migration, or another shared workflow origin still needs it. An exact update retry is a no-op even when it carries the pre-update expected version.

Use DELETE /v1/spatial/shared-clusters/{workflowId} with expectedAccessConcurrencyVersion and an optional bounded reason to revoke the workflow. Revocation removes only its origins. If the workflow originally created the membership, guarded cleanup removes that shell only when no other active shared workflow, direct grant, permission group, protected-owner relation, active invitation, or promoted role still needs it. An exact retry of the same terminal revocation returns the current revoked state.

Lifecycle responses include the workflow and resource identities, status, createdMembership, presentation-only isSharedAccess and isSharedAccessGuest, access concurrency version, committed permission version, and the normalized contribution set for create/update. Create, update, revoke, and membership cleanup have distinct audit/outbox event types.

Before rendering a manager's existing workflow list, call GET /v1/spatial/shared-clusters/managed?organizationId={organizationId}&clusterId={clusterId}. This is an authorized Cluster-management read, not recipient discovery. It returns only active workflows for that exact Cluster, including target user, access concurrency version, membership-purpose flag, and active normalized contributions. Use those versions for subsequent PUT or DELETE requests and refresh the list after 409 Conflict.

Read Effective Access

Call GET /v1/spatial/clusters/{clusterId}/permissions to retrieve independent final capability decisions projected as compatibility flags:

  • canRead requires ClusterView=Read.
  • canUpload requires ClusterDocuments=Write.
  • canManageCluster requires ClusterUpdate=Allow.
  • canManageShares requires ManageResourcePermissions=Allow at the cluster.
  • the applicable legacy shareRole, when present

The flags are not inferred from one broad role or write boolean. For example, a user can manage cluster metadata without being allowed to manage access, or can read cluster documents without being allowed to upload them. A returned legacy shareRole describes the compatibility source; it does not bypass the final evaluator decisions. Use these values to guide the interface, but treat the API response to each mutation as authoritative.

Authenticated Cluster Shares

The authenticated sharing routes under /v1/spatial/clusters/{clusterId}/shares are deprecated. Their OpenAPI operations are marked deprecated and clients should move access provisioning to organization membership plus group/direct permission management.

Legacy share rows are now read-only:

  • GET /v1/spatial/clusters/{clusterId}/shares remains available for compatibility inventory.
  • POST /v1/spatial/clusters/{clusterId}/shares rejects all new legacy share rows.
  • PATCH /v1/spatial/clusters/{clusterId}/shares/{shareId} rejects legacy role changes.
  • DELETE /v1/spatial/clusters/{clusterId}/shares/{shareId} remains available to revoke an exact existing row as access-reducing cleanup.

Only existing direct-user Contributor rows remain compatibility authorization sources. They retain their current cluster read/write behavior, including the existing job and external-link write paths, without cluster lifecycle or share-management authority. Viewer and organization-target legacy rows are not authorization or migration sources. Do not build new integrations around these deprecated routes.

External anonymous links are a separate mechanism. Do not add a user to an organization merely because they opened an external link.

On this page