> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parable.work/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace Pool

> Query live Workspace membership, Provider, role, Parable, Plot, and run metadata.

The Workspace Pool is a live, query-safe projection of control-plane metadata.
It is not a copied data-lake layer. Reads see the current Workspace state at
query time.

```text theme={null}
workspace.{table}
```

## Tables

Availability says whether a table is served today. A planned table is not
referenceable yet: a query that names it fails at validation with
`PV_REF_002`.

| Table                                         | Contents                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Availability |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| `workspace.users`                             | Workspace members: user ID, membership ID, email, name, title, membership status and when it changed, joined-at, timezone and locale (membership and profile), work function, and work role. Every membership status is served, `REVOKED` included; filter on `status` for current members.                                                                                                                                                                                                                                               | Available    |
| `workspace.providers`                         | Connected Providers: instance ID, the label the Workspace gave it, the Provider definition's slug and name, connection status (with its last change and message), whether it has ever synced, when and by whom it was disabled, and created and updated timestamps. Disabled instances are served; active Providers are `disabled_at IS NULL AND connection_status IS DISTINCT FROM 'disabled'`. `id` joins the per-connector config tables and `parent_data_filters` on `tenant_connector_id`; `disabled_by` joins `workspace.users.id`. | Available    |
| `workspace.provider_connections`              | Streams configured on each Provider connection: stream ID, the connection it belongs to (`provider_id` joins `workspace.providers.id`), name, the platform tap it is bound to, description, kind (`override`, `custom`, `artifact`), whether it is enabled and since when, whether it is a canonical person source, the artifact behind an artifact stream, and created and updated timestamps. Disabled streams are served with `enabled = false`; soft-deleted streams are not.                                                         | Available    |
| `workspace.{connector}_ingestion_config`      | Non-secret ingestion settings of each connected Provider, one table per connector.                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Available    |
| `workspace.{connector}_authentication_config` | Non-secret authentication settings of each connected Provider, one table per connector. Credential material is never served.                                                                                                                                                                                                                                                                                                                                                                                                              | Available    |
| `workspace.parent_data_filters`               | The parent-data allowlist per Provider stream, one row per allowlisted value.                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Available    |
| `workspace.roles`                             | Roles held by at least one Workspace member: role ID, name, description, parent role ID, and created and updated timestamps. One row per role however many members hold it. Role names are display values and can change; `workspace.role_permissions` is the durable capability signal. A role no member holds is not listed, so a `parent_role_id` can name a role absent from the table.                                                                                                                                               | Available    |
| `workspace.role_permissions`                  | Permission strings directly granted to each role held by at least one Workspace member: role ID, permission, and granted-at. One row per grant. Direct grants only: a role also inherits its parent chain, and a permission covers its dotted descendants.                                                                                                                                                                                                                                                                                | Available    |
| `workspace.user_roles`                        | Role assignments for Workspace members: user ID, membership ID, role ID, assigned-at, and assigned-by. One row per (member, role) pair, keyed by user ID so it joins `workspace.users.id` and by role ID so it joins `workspace.roles.id`. Assignments of every membership status are served; join `workspace.users.status` to filter.                                                                                                                                                                                                    | Available    |
| `workspace.parables`                          | Parables in the Workspace.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Planned      |
| `workspace.sql_plots`                         | Plot slugs, schedules, and enabled state.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Planned      |
| `workspace.query_runs`                        | Plot run history and spend.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Planned      |
| `workspace.preferences`                       | All currently permitted Preference records for the selected Parable and version context; queries choose records explicitly.                                                                                                                                                                                                                                                                                                                                                                                                               | Available    |

Secrets, API tokens, sessions, and credential payloads are never exposed in
this pool.

## List active Providers

Every connected Provider instance is a row, disabled ones included, so
"active" is a predicate rather than a hidden rule:

```sql theme={null}
SELECT id, name, connector_slug, connection_status, has_ever_synced
FROM workspace.providers
WHERE disabled_at IS NULL
  AND connection_status IS DISTINCT FROM 'disabled'
ORDER BY connector_slug, name
```

`connector_slug` is the `{connector}` of `providers.{connector}.{table}`, and
`connection_status` carries its enum values in the served schema. `id` is the
`tenant_connector_id` of the per-connector config tables, so an instance's
settings and allowlist sit one join away:

```sql theme={null}
SELECT
  provider.connector_slug,
  provider.name,
  filter.parent_tap,
  filter.field,
  filter.value
FROM workspace.providers AS provider
JOIN workspace.parent_data_filters AS filter
  ON filter.tenant_connector_id = provider.id
WHERE provider.disabled_at IS NULL
  AND provider.connection_status IS DISTINCT FROM 'disabled'
ORDER BY provider.connector_slug, filter.parent_tap, filter.value
```

## List a Provider's streams

Every stream configured on a Provider connection is a row, disabled ones
included, so "enabled" is a column rather than a hidden rule. Join to
`workspace.providers` on `provider_id` to name the connector and to drop the
streams of disabled or deleted connections:

```sql theme={null}
SELECT p.connector_slug, s.name AS stream, s.kind, s.enabled, s.enabled_since
FROM workspace.provider_connections AS s
JOIN workspace.providers AS p ON p.id = s.provider_id
WHERE p.disabled_at IS NULL
ORDER BY p.connector_slug, s.name
```

`kind` carries its enum values in the served schema. An enabled `override`
stream is served as `providers.{connector_slug}.{platform_tap_name}` and an
enabled `custom` stream as `providers.{connector_slug}.{name}`, both
lowercased; a disabled stream has no Provider table. An enabled `artifact`
stream is served as `artifacts.{name}` unless the upload was published under
another slug, and `artifact_id` names the upload behind it. Streams of a
disabled or deleted connection stay rows of `provider_connections` and drop
out through the join above.

## Roles are platform-level and scoped through membership

Roles are permission bundles defined once for the platform, not per
Workspace. `workspace.roles` lists the roles that at least one member of the
Workspace holds, with every membership status counted. Because the name is a
display value an administrator can change, join on `id`, not `name`:

```sql theme={null}
SELECT
  child.name,
  parent.name AS parent_name
FROM workspace.roles AS child
LEFT JOIN workspace.roles AS parent
  ON parent.id = child.parent_role_id
ORDER BY child.name
```

`parent_name` is `NULL` both when a role has no parent and when no member of
the Workspace holds the parent; `child.parent_role_id` still carries the id in
the second case.

## Permissions cover their descendants

`workspace.role_permissions` lists the permission strings granted directly to
each role in `workspace.roles`. A permission is a dotted path, and a grant
covers every descendant of its path: a role granted `tenant` can do
everything under `tenant.`, including `tenant.users.manage`. The table does
not expand that hierarchy, and it does not fold in the parent chain a role
inherits through `parent_role_id`; both are one predicate away. To list the
`tenant`-scoped permissions each held role grants:

```sql theme={null}
SELECT
  r.name AS role,
  rp.permission
FROM workspace.roles AS r
JOIN workspace.role_permissions AS rp
  ON rp.role_id = r.id
WHERE rp.permission = 'tenant'
  OR starts_with(rp.permission, 'tenant.')
ORDER BY r.name, rp.permission
```

To ask the question the other way round, which held roles cover a specific
permission, compare the target against each grant plus a trailing dot:

```sql theme={null}
SELECT DISTINCT r.name AS role
FROM workspace.roles AS r
JOIN workspace.role_permissions AS rp
  ON rp.role_id = r.id
WHERE rp.permission = 'tenant.users.manage'
  OR starts_with('tenant.users.manage', rp.permission || '.')
ORDER BY r.name
```

Join on `role_id`, never on the role name, and join through `workspace.roles`
to reach the parent chain when a role inherits from another. Which members
hold a role is `workspace.user_roles`.

## Who holds each role

`workspace.user_roles` is the assignment edge: one row per (member, role)
pair. `user_id` joins `workspace.users.id` and `role_id` joins
`workspace.roles.id`, so the members page is a three-way join:

```sql theme={null}
SELECT
  u.email,
  u.status,
  r.name AS role,
  ur.assigned_at
FROM workspace.user_roles AS ur
JOIN workspace.users AS u
  ON u.id = ur.user_id
JOIN workspace.roles AS r
  ON r.id = ur.role_id
ORDER BY u.email, r.name
```

Assignments are served for every membership status, `SUSPENDED` and
`REVOKED` included, so filter on `u.status = 'ACTIVE'` to see who can act
today. An assignment of a role that has since been deleted is still a row in
`workspace.user_roles`; the join to `workspace.roles` drops it, because that
table lists only live roles. `assigned_by` is the user ID of the person who
made the assignment and may name someone who has since left the Workspace,
so a `LEFT JOIN` to `workspace.users` is the right shape for it. To count
members per role:

```sql theme={null}
SELECT
  r.name AS role,
  count(*) AS members
FROM workspace.roles AS r
JOIN workspace.user_roles AS ur
  ON ur.role_id = r.id
GROUP BY r.name
ORDER BY r.name
```

## Read your Preferences from a Plot

`workspace.preferences` returns all records admitted by the current Workspace,
Parable, actor, and version boundaries. It does not rank scopes or select one
record per slot. A Person, Workspace, and authored record can all be visible
for the same slot:

```sql theme={null}
SELECT id, handle, scope, actor_id, ref_id, commit_id, value, revision
FROM workspace.preferences
WHERE handle = 'salesQuery'
```

Select the desired `id`, or filter explicitly by scope, actor, slot, and
ref/commit context. The `id` identifies the current stored row; rebuilding
an authored projection may replace it. If a request names both a draft and
a commit, their admitted records remain distinct rather than one winning.
Existing SQL that assumed a scalar result per handle must now make its
selection explicit.

Other actors' Person-scope rows remain hidden by the current access rule.
Custom Protection sharing has not replaced that boundary yet. The legacy
Preference SDK/API `effective[]` resolver also retains its scope fallback;
this SQL projection no longer performs that selection.

Reads are live: a Plot re-run after a Preference changes sees the new value,
and a run records this table as unpinned. Because the rows depend on the
person running the Plot, a scheduled Plot cannot read `workspace.preferences`
and is refused at publish and schedule time.

## Declared projections

Tables below are generated from psgen `@projection` declarations in
`platform-schemas/services/web-db`: the same declaration produces the
Postgres view, the Arrow schema the query layer serves, and this block.

Generated from psgen `@projection` declarations; edit the schema, not this block.

| Table                            | Contents                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `workspace.preferences`          | All visible Preference records; id identifies each record and refId/commitId distinguish simultaneously visible authored versions. Applications select values.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `workspace.provider_connections` | Streams of the current Workspace's Provider connections: one row per tenant tap (an override of a platform tap, a custom tap, or an artifact-derived tap), joined to the platform tap it is bound to. Disabled streams are served with enabled = false so "enabled" is explicit in the consumer's SQL; soft-deleted streams are not. Streams of a disabled or deleted connection are served too and drop out through the join to workspace.providers. The platform tap is reference data and is never filtered on its own soft-delete. Served by the query layer as workspace.provider\_connections (EDR-0081).                               |
| `workspace.providers`            | Connected Providers of the current Workspace: one row per connector instance, joined to the Provider definition it was created from. Disabled instances are served with disabled\_at set so "active" is explicit in the consumer's SQL; soft-deleted instances are not. The definition is global reference data and is never filtered on its own soft-delete: a deleted definition with live instances is an inconsistency worth seeing. Served by the query layer as workspace.providers (EDR-0081).                                                                                                                                         |
| `workspace.role_permissions`     | Permission strings directly granted to each role held in the current Workspace: one row per grant on a platform role that at least one member holds, whatever the membership status. Direct grants only: a role also inherits its parent chain (workspace.roles.parent\_role\_id), and a permission covers every dotted descendant of itself ('tenant' covers 'tenant.users.manage'), so a coverage question is permission = 'x' OR starts\_with(permission, 'x.'). Grants of soft-deleted roles and of roles held only through soft-deleted memberships are not served. Served by the query layer as workspace.role\_permissions (EDR-0081). |
| `workspace.roles`                | Roles held in the current Workspace: one row per platform role that at least one member holds, whatever the membership status. Roles are platform-level permission bundles with no Workspace of their own, so a role no member holds is not served, and a parent\_role\_id may name a role that is absent from this table. Soft-deleted roles and memberships are not served. Served by the query layer as workspace.roles (EDR-0081).                                                                                                                                                                                                        |
| `workspace.user_roles`           | Role assignments for members of the current Workspace: one row per (member, role) pair, keyed by the person's user id so it joins workspace.users directly and by role id so it joins workspace.roles. Assignments of every membership status are served, SUSPENDED and REVOKED included; join workspace.users.status to filter. Assignments of soft-deleted memberships are not served. Served by the query layer as workspace.user\_roles (EDR-0081).                                                                                                                                                                                       |
| `workspace.users`                | Members of the current Workspace: one row per membership, joined to the person's profile. Every membership status is served, including REVOKED, so a query can reconcile who left; soft-deleted memberships and profiles are not. Served by the query layer as workspace.users (EDR-0081).                                                                                                                                                                                                                                                                                                                                                    |

### `workspace.preferences`

All visible Preference records; id identifies each record and refId/commitId distinguish simultaneously visible authored versions. Applications select values.

| Column         | Type                         | Nullable | Contents                                                                                                                                                  |
| -------------- | ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `parable`      | `Parable.Handle`             | no       | The handle of the Parable the row belongs to.                                                                                                             |
| `actor_kind`   | `ParableActorKindEnum`       | yes      | Optional actor association. Policies determines visibility.                                                                                               |
| `actor_id`     | `Identity.UUID`              | yes      |                                                                                                                                                           |
| `scope`        | `ParablePreferenceScopeEnum` | no       | Definition provenance retained for historical projections, not privacy.                                                                                   |
| `use_id`       | `Identity.UUID`              | yes      | Schema and explicit Use selection, independent of actor association.                                                                                      |
| `pane_key`     | `Identity.UUID`              | no       |                                                                                                                                                           |
| `owner_key`    | `Identity.UUID`              | yes      |                                                                                                                                                           |
| `policies_key` | `Identity.UUID`              | no       | Current Source Use authority for definition values; ordinary records keep their own Policies. Stored historical source metadata is not rewritten.         |
| `is_default`   | `BOOLEAN`                    | no       |                                                                                                                                                           |
| `slot_key`     | `Identity.UUID`              | no       | Historical definition/instance association. New records use their own id.                                                                                 |
| `handle`       | `Parable.Handle`             | no       | An optional readable handle for explicit record selection.                                                                                                |
| `kind`         | `ParableSlotKindEnum`        | no       | Transitional definition tag; paneKey supplies the native value contract.                                                                                  |
| `value`        | `Generic.JSON`               | no       | Native value validated by the Pane selected by paneKey; JSON null is legal.                                                                               |
| `revision`     | `Generic.Int64`              | no       | The compare-and-swap token the API carries for this row.                                                                                                  |
| `updated_at`   | `Temporal.DateTime`          | no       |                                                                                                                                                           |
| `id`           | `Identity.UUID`              | no       | Stable Preference record identity. Use and actor association are explicit metadata; definition ref/commit preserve provenance without selecting a winner. |
| `ref_id`       | `Identity.UUID`              | yes      | Pinned definition provenance for a draft record; null for live Uses/values.                                                                               |
| `commit_id`    | `Identity.UUID`              | yes      | Pinned definition provenance for a committed record; null for live Uses/values.                                                                           |

Rows are scoped by the session settings `platform.workspace_id`, `platform.parable_root_ref`, `platform.actor_id`, `platform.actor_kind`, `platform.preference_filter_protocol`, `platform.parable_ref`, `platform.parable_commit`; the query layer sets them for every scan.

### `workspace.provider_connections`

Streams of the current Workspace's Provider connections: one row per tenant tap (an override of a platform tap, a custom tap, or an artifact-derived tap), joined to the platform tap it is bound to. Disabled streams are served with enabled = false so "enabled" is explicit in the consumer's SQL; soft-deleted streams are not. Streams of a disabled or deleted connection are served too and drop out through the join to workspace.providers. The platform tap is reference data and is never filtered on its own soft-delete. Served by the query layer as workspace.provider\_connections (EDR-0081).

| Column                    | Type                         | Nullable | Contents                                                                                                                                                                                                                                                                   |
| ------------------------- | ---------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                      | `Identity.UUID`              | no       | The stream id (the Workspace's tap row).                                                                                                                                                                                                                                   |
| `provider_id`             | `Identity.UUID`              | no       | The Provider connection the stream belongs to; joins workspace.providers.id.                                                                                                                                                                                               |
| `name`                    | `Identity.Name`              | no       | Stream name as the Workspace knows it. For an override it matches platform\_tap\_name; for a custom stream it is the {table} in providers.{connector}.{table}; for an artifact stream it is the artifacts.{table} name unless the upload was published under another slug. |
| `platform_tap_name`       | `Tap.Identifier`             | no       | Name of the platform tap the stream is bound to: for an override, the {table} in providers.{connector}.{table}. Every artifact stream binds to the Artifact connector's shared platform tap.                                                                               |
| `description`             | `TEXT`                       | yes      | Description of what the stream extracts, when set.                                                                                                                                                                                                                         |
| `kind`                    | `TenantConnectorTapKindEnum` | no       | override (narrows a platform tap), custom (a per-Workspace implementation whose schema may diverge), or artifact (derived from an uploaded file).                                                                                                                          |
| `enabled`                 | `BOOLEAN`                    | no       | Whether the stream is enabled. Disabled streams are served here but are not synced and are not listed in the Provider catalog.                                                                                                                                             |
| `enabled_since`           | `Temporal.DateTime`          | yes      | Start of the current uninterrupted enabled period; null when the stream is disabled or its enabled period predates tracking.                                                                                                                                               |
| `canonical_person_source` | `BOOLEAN`                    | no       | Whether the stream is one of the Workspace's canonical person sources for identity resolution.                                                                                                                                                                             |
| `artifact_id`             | `Identity.UUID`              | yes      | The uploaded artifact behind an artifact stream; null otherwise.                                                                                                                                                                                                           |
| `created_at`              | `Temporal.DateTime`          | no       | When the stream was created.                                                                                                                                                                                                                                               |
| `updated_at`              | `Temporal.DateTime`          | no       | When the stream was last updated.                                                                                                                                                                                                                                          |

Rows are scoped by the session settings `platform.workspace_id`; the query layer sets them for every scan.

### `workspace.providers`

Connected Providers of the current Workspace: one row per connector instance, joined to the Provider definition it was created from. Disabled instances are served with disabled\_at set so "active" is explicit in the consumer's SQL; soft-deleted instances are not. The definition is global reference data and is never filtered on its own soft-delete: a deleted definition with live instances is an inconsistency worth seeing. Served by the query layer as workspace.providers (EDR-0081).

| Column                         | Type                        | Nullable | Contents                                                                                                                                                             |
| ------------------------------ | --------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                           | `Identity.UUID`             | no       | The connector instance id; the join key to workspace.{connector}\_ingestion\_config.tenant\_connector\_id and workspace.parent\_data\_filters.tenant\_connector\_id. |
| `name`                         | `Identity.Name`             | yes      | Label the Workspace gave the instance, when set; the app shows "Connection" otherwise.                                                                               |
| `connector_slug`               | `Connector.Slug`            | no       | Provider definition slug: the {connector} in providers.{connector}.{table}.                                                                                          |
| `connector_name`               | `Identity.Name`             | no       | Provider definition display name.                                                                                                                                    |
| `connection_status`            | `TenantConnectorStatusEnum` | yes      | Operational status the platform last computed for the instance (idle, initial\_syncing, sync\_failed, disabled, credential\_error, ...), when set.                   |
| `connection_status_updated_at` | `Temporal.DateTime`         | yes      | When connection\_status last changed; a stale value is worth noticing.                                                                                               |
| `connection_status_message`    | `TEXT`                      | yes      | Human-readable detail for the current status, when set.                                                                                                              |
| `has_ever_synced`              | `BOOLEAN`                   | no       | True once at least one sync has completed for the instance.                                                                                                          |
| `disabled_at`                  | `Temporal.DateTime`         | yes      | When the instance was disabled; non-null means disabled.                                                                                                             |
| `disabled_by`                  | `Identity.UserID`           | yes      | The user who disabled the instance; joins workspace.users.id.                                                                                                        |
| `created_at`                   | `Temporal.DateTime`         | no       | When the instance was created.                                                                                                                                       |
| `updated_at`                   | `Temporal.DateTime`         | no       | When the instance was last updated.                                                                                                                                  |

Rows are scoped by the session settings `platform.workspace_id`; the query layer sets them for every scan.

### `workspace.role_permissions`

Permission strings directly granted to each role held in the current Workspace: one row per grant on a platform role that at least one member holds, whatever the membership status. Direct grants only: a role also inherits its parent chain (workspace.roles.parent\_role\_id), and a permission covers every dotted descendant of itself ('tenant' covers 'tenant.users.manage'), so a coverage question is permission = 'x' OR starts\_with(permission, 'x.'). Grants of soft-deleted roles and of roles held only through soft-deleted memberships are not served. Served by the query layer as workspace.role\_permissions (EDR-0081).

| Column       | Type                 | Nullable | Contents                                                                                                                                                                          |
| ------------ | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `role_id`    | `Identity.UUID`      | no       | The role the permission is granted to; joins workspace.roles.id.                                                                                                                  |
| `permission` | `Parable.Permission` | no       | The permission string, a dotted path such as 'tenant.users.manage'. A permission covers its descendants: compare with equality or a starts\_with on the path plus a trailing dot. |
| `granted_at` | `Temporal.DateTime`  | no       | When the permission was granted to the role.                                                                                                                                      |

Rows are scoped by the session settings `platform.workspace_id`; the query layer sets them for every scan.

### `workspace.roles`

Roles held in the current Workspace: one row per platform role that at least one member holds, whatever the membership status. Roles are platform-level permission bundles with no Workspace of their own, so a role no member holds is not served, and a parent\_role\_id may name a role that is absent from this table. Soft-deleted roles and memberships are not served. Served by the query layer as workspace.roles (EDR-0081).

| Column           | Type                | Nullable | Contents                                                                                                                                |
| ---------------- | ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `id`             | `Identity.UUID`     | no       | The role id; stable across renames, and the join key to workspace.user\_roles.role\_id and workspace.role\_permissions.role\_id.        |
| `name`           | `TEXT`              | no       | Display name of the role. Administrators can rename a role, so the name is not a durable capability signal; the role's permissions are. |
| `description`    | `TEXT`              | yes      | Description of the role, when set.                                                                                                      |
| `parent_role_id` | `Identity.UUID`     | yes      | The role this one was derived from, when set. The parent is served only if a member of this Workspace holds it.                         |
| `created_at`     | `Temporal.DateTime` | no       | When the role was created.                                                                                                              |
| `updated_at`     | `Temporal.DateTime` | no       | When the role was last updated.                                                                                                         |

Rows are scoped by the session settings `platform.workspace_id`; the query layer sets them for every scan.

### `workspace.user_roles`

Role assignments for members of the current Workspace: one row per (member, role) pair, keyed by the person's user id so it joins workspace.users directly and by role id so it joins workspace.roles. Assignments of every membership status are served, SUSPENDED and REVOKED included; join workspace.users.status to filter. Assignments of soft-deleted memberships are not served. Served by the query layer as workspace.user\_roles (EDR-0081).

| Column          | Type                | Nullable | Contents                                                                                                 |
| --------------- | ------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `user_id`       | `Identity.UUID`     | no       | The person's user id; joins workspace.users.id.                                                          |
| `membership_id` | `Identity.UUID`     | no       | The membership row the assignment belongs to; joins workspace.users.membership\_id.                      |
| `role_id`       | `Identity.UUID`     | no       | The assigned role; joins workspace.roles.id.                                                             |
| `assigned_at`   | `Temporal.DateTime` | no       | When the role was assigned.                                                                              |
| `assigned_by`   | `Identity.UserID`   | no       | Who assigned the role; joins workspace.users.id, and may name a member who has since left the Workspace. |

Rows are scoped by the session settings `platform.workspace_id`; the query layer sets them for every scan.

### `workspace.users`

Members of the current Workspace: one row per membership, joined to the person's profile. Every membership status is served, including REVOKED, so a query can reconcile who left; soft-deleted memberships and profiles are not. Served by the query layer as workspace.users (EDR-0081).

| Column              | Type                  | Nullable | Contents                                                                                                                     |
| ------------------- | --------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `id`                | `Identity.UUID`       | no       | The person's user id; the same person has the same id in every Workspace.                                                    |
| `membership_id`     | `Identity.UUID`       | no       | The membership row this Workspace holds for the person.                                                                      |
| `email`             | `Contact.Email`       | no       | Sign-in email of the person (case-insensitive); the join key to Provider person columns that carry the Contact.Email scalar. |
| `name`              | `Identity.Name`       | no       | Display name from the person's profile.                                                                                      |
| `title`             | `TEXT`                | yes      | Job title from the person's profile, when set.                                                                               |
| `status`            | `TenantUserStatus`    | no       | Membership status: PENDING, ACTIVE, SUSPENDED, or REVOKED.                                                                   |
| `status_changed_at` | `Temporal.DateTime`   | yes      | When the membership status last changed.                                                                                     |
| `joined_at`         | `Temporal.DateTime`   | no       | When the membership was created.                                                                                             |
| `timezone`          | `Temporal.TimeZone`   | yes      | Timezone the member chose for this Workspace (IANA), when set.                                                               |
| `locale`            | `Localization.Locale` | yes      | Locale the member chose for this Workspace (BCP 47), when set.                                                               |
| `profile_timezone`  | `Temporal.TimeZone`   | yes      | Timezone from the person's profile (IANA), when set.                                                                         |
| `profile_locale`    | `Localization.Locale` | yes      | Locale from the person's profile (BCP 47), when set.                                                                         |
| `work_function`     | `WorkFunction`        | yes      | Functional area captured at signup, when set.                                                                                |
| `work_role`         | `WorkRole`            | yes      | Seniority captured at signup, when set.                                                                                      |

Rows are scoped by the session settings `platform.workspace_id`; the query layer sets them for every scan.

## Join live metadata to Provider data

`workspace.users.email` and Provider columns with the `Contact.Email` semantic
scalar represent compatible values:

```sql theme={null}
SELECT
  member.email,
  member.name,
  directory.orgunitpath,
  directory.isadmin,
  directory.suspended
FROM workspace.users AS member
LEFT JOIN providers.google.users AS directory
  ON directory.primaryemail = member.email
```

This query compares current Workspace membership with the most recently synced
Provider directory state. Their freshness models differ: Workspace rows are
live at query time, while Provider rows update when the corresponding stream
syncs.

Because Workspace rows are read live, a scheduled Plot that references a
`workspace.*` table records that input as unpinned in its run history: the run
says what it read and when, and a replay reads the current state rather than
the original rows.

Use [catalog discovery](/protocols/sql/data-catalog/discover-tables-and-columns)
for the current table schemas and
[types and metadata](/protocols/sql/sql-behavior/types-and-metadata) before
joining semantic values.
