workspaces

Update a workspace's labels

display_name, name and description are the mutable fields. An omitted field is unchanged; description: null clears it.

A display_name edit re-derives name, the machine handle, by slugifying the new label — unless the body sets name explicitly. A label with nothing sluggable in it leaves the handle alone. The handle must be free among the live workspaces of the same tree, not the whole account, so one person's personal workspace name never denies another's; if it is taken the request is a 409 and the fix is to resend with an explicit name.

A handle change is a one-way door for clients: name is the marketplace plugin slug's source, so a rename orphans every existing install. The org_default and user_default handles do not move — Control Plane provisioning matches those two by value — and asking is a 409; their display_name is still editable.

parent_id moves the workspace and its subtree under another shared-tree workspace. It takes update on the workspace and create on the destination, the same receipt a create under that parent takes: a destination the caller cannot see is a 404, one it can see but not create in is a 403. Restating the current parent asks nothing.

An archived workspace does not change: relabelling it, moving it, or moving another workspace under it is 409 workspace_archived. The archive renamed it to free its handle, and a relabel must not take the handle back.

patch/registry/v1/workspaces/{workspace_id}

Request

  • Base URL: https://api.atlan.com (the document declares no server; this is the origin it was published from)
  • URL: https://api.atlan.com/registry/v1/workspaces/{workspace_id}
  • Auth: HTTP bearer

Path parameters

workspace_idstring required

Workspace handle (workspace_…) or the workspace's kebab-case name. The two grammars are disjoint, so no disambiguation is needed. A name is mutable — a display_name edit re-derives it — so a client that stores an address should store the handle, which every response echoes.

Headers

Content-Encoding'base64' | 'gzip, base64'

Codings applied to the request body, outermost last. Send base64, or gzip, base64 to compress first; the body is decoded before it is read and Content-Type still names the inner media type. Omit for an unencoded body.

Request body

descriptionstring nullable

Absent leaves it unchanged; null clears it. Option<Option<_>> with deserialize_with is what keeps those two apart — a plain Option collapses them, which is the bug PATCH-is-the-primary-verb exists to prevent.

display_namestring nullable

Human-readable name.

namestring nullable

The machine handle: kebab-case, at most 40 characters. Prose is a 400, never silently re-slugged.

Omit it and a display_name edit derives it from the new label (naming-2026-08-21-display-name-rename-reslugs-the-handle). Set it to keep a hand-picked handle across a relabel, or to resolve the 409 a derived handle collided on.

A handle change is a one-way door for clients: name is the marketplace plugin slug's source, so renaming orphans every existing install of this workspace's plugin. The two provisioning handles — org_default and user_default — refuse it outright.

parent_idstring nullable

Move the workspace under another one, with its whole subtree.

Applied before the label patch and in its own transaction, alongside the rerooting of every project in the moved subtree — which has to be the same commit, because project names are unique per tree root.

Refused for a singleton (a tree root does not move), for a destination inside the moved subtree (a cycle), and for a move that would nest the subtree's leaves past the depth cap.

Response

OK

artifact_countinteger nullable

How many artifacts this workspace holds, and only when include=artifact_counts asked for it — then on every row of the page, 0 included.

The discover-band count the pre-Postgres stack served unconditionally and the cutover dropped: it is the affordance that lets someone decide whether a workspace is worth joining, and its absence made every unjoined row read "0 skills". The inverse of member_count's absent-rather-than-zero rule, and deliberately so — a roster size is unknowable at discover, an artifact count is not, because it names nobody (registry_tenancy::workspace::artifact_counts).

artifact_count_by_kindobject nullable

The per-kind breakdown of [Self::artifact_count], which it sums to. Attached and absent with it, always — two fields, one embed, so a client never sees a total it cannot explain.

A kind with no artifacts here is absent rather than 0: the map is what the workspace holds, not the kind registry. A BTreeMap, so the key order is stable across responses.

created_atstring required

Value of the created_at field.

created_bystring required

Value of the created_by field.

descriptionstring nullable

Human-readable description.

display_namestring required

Human-readable name.

idstring required

Stable identifier of this resource.

kindstring required

Domain discriminator for this resource.

member_countinteger nullable

Roster size, and only when include=member_count asked for it.

Absent rather than 0 when unrequested, so a client cannot read a missing embed as an empty workspace. Also absent for a workspace the caller can discover but not read: the count is taken under a Read receipt on the membership rows while this list runs under Discover, so a headcount is never a thing discovery alone reveals (registry_tenancy::workspace::member_counts).

namestring required

Stable machine-readable name.

org_wideboolean required

Whether this workspace sits in the account's shared tree, and so is org-discoverable and self-joinable.

Derived from the tree root, never stored, so a reparent moves it with the row. Not a published field upstream — it is this stack's convenience — but it has to be right, and it was not: the previous derivation was root_workspace_id != id || kind == OrgDefault, which is true for every non-root workspace, including every child of a personal tree. A workspace inside someone's private space rendered as org-wide.

parent_idstring nullable

None for a singleton, which is a tree root.

permissionsGatewayCapabilityMatrix

The caller's advisory can_* capability matrix on a resource.

Advisory only: it reflects what the caller may do, and is for a UI to gate controls on. It never authorizes a request — the platform decides that independently on every call.

Deliberately an open map rather than a typed struct. The authoritative permission tables are internal to the platform core, and lifting them into the SDK would put an authorization decision surface into every extension. An extension reads this matrix; it never derives one.

root_workspace_idstring required

Stable identifier of the related root workspace resource.

updated_atstring required

Value of the updated_at field.

updated_bystring required

Value of the updated_by field.

visibilitystring required

public or private — how open this shared-tree workspace is: whether the account can list it and self-join it. Meaningful only when org_wide is true; a personal-tree workspace is always private. Set on create (inherits the parent when omitted) and changed by PATCH /workspaces/{id}/visibility.

workspace_statusstring required

The lifecycle state.

workspace_status, not status: it is the published spelling, and it is the name the filter grammar already uses (registry_model::workspace::Entity's Field is workspace_status). Serving status while accepting filter[workspace_status] meant a client read a field it could not filter on.

Changes

No recorded changes to this operation across all 1 revision of this API.