--- name: platform-dataspace-access-configure description: "Use this skill to configure Salesforce Data Cloud DataSpace access for permission sets. Grants dataspace-level access via MDAPI PermissionSet XML with dataspaceScopes elements, and optionally grants object-level access to specific DMO, DLO, or CIO objects via the Object Access Grants Connect API. TRIGGER when: user needs to create or update a permission set that includes DataSpace access, grant a permission set access to a specific dataspace, configure dataAccessLevel or objectAccessLevel for a dataspace scope, add RBAC object access grants for Data Cloud objects, or list or remove object access grants for a permission set and DataSpace pair. DO NOT TRIGGER when: the task is a generic permission set without any dataspace access (use platform-permission-set-generate), the request is about data ingestion or streams (use data360-prepare), or the work involves creating dataspaces themselves rather than granting access to them." metadata: version: "1.0" minApiVersion: "67.0" cliTools: - tool: ["sf"] semver: ">=2.0.0" - tool: ["jq"] semver: ">=1.6" --- # platform-dataspace-access-configure Configure DataSpace access in Salesforce Data Cloud using a two-layer model: 1. **DataSpace-level access** — grant a `PermissionSet` access to a DataSpace by embedding a `` element in the permission set XML and deploying via MDAPI. 2. **Object-level access (optional)** — grant that permission set access to specific DMO / DLO / CIO objects within the DataSpace using the Object Access Grants Connect API. The MDAPI layer is required to establish the PermissionSet → DataSpace linkage. The Connect API layer is optional and only needed when access should be scoped to specific objects rather than governed entirely by data governance policies. --- ## Decide the Case First Pick exactly one case from the table below before writing any files. Each case has a different output shape. | Case | User intent | Permission set state | Files to emit | |---|---|---|---| | **A. Create new permset with DS access** | "create a permission set called X with dataspace scope Y" | does NOT exist yet | `permissionsets/.permissionset-meta.xml` **and** `package.xml` | | **B. Add DS access to existing permset** | "grant existing permission set X access to dataspace Y" | already deployed (may contain other permissions) | patched `permissionsets/.permissionset-meta.xml` **and** `package.xml` — see Case B workflow below | | **C. Object-level grant only** | "grant permset X access to object Z (in dataspace Y)" — permset + scope already configured | already deployed with `dataspaceScopes` | `api-request.json` (Connect API body). NO permission set XML, NO `package.xml` | Only emit the files listed for the case you picked. Emitting Case A/B files for a Case C prompt (or vice versa) is a correctness failure — extra files change the deployment shape. > **Case B — critical:** PermissionSet MDAPI deploy is a **full metadata replace**. Every ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, ``, `` element you omit from the redeploy is **deleted from the org**. Before adding `` to an existing permset, retrieve the current XML and patch it — do not hand-author from scratch. ### Case B workflow 1. Retrieve the existing permission set: ```bash sf project retrieve start --metadata PermissionSet: --target-org ``` 2. Open the retrieved `permissionsets/.permissionset-meta.xml`. Keep every element already there. 3. Insert the `` block for the target DataSpace (element order in the file does not matter for MDAPI). If the file already has a `` block **for this same DataSpace**, replace only that block. Leave every `` block for other DataSpaces untouched — one block per DataSpace, and removing a block revokes that DataSpace grant. 4. Write `package.xml` listing the permset in ``. 5. Redeploy with `sf project deploy start`. --- ## When This Skill Owns the Task Trigger this skill when the user wants to: - Create a permission set that grants access to a Data Cloud DataSpace - Add or modify `dataspaceScopes` on an existing permission set - Grant a permission set access to specific DMO / DLO / CIO objects in a DataSpace - Configure `dataAccessLevel` and `objectAccessLevel` for a DataSpace scope - List or remove object access grants for a permission set + DataSpace pair Delegate elsewhere when: - The permission set has no DataSpace access at all → `platform-permission-set-generate` - The task is creating the DataSpace itself → `data360-orchestrate` - The task is ingesting data or configuring streams → `data360-prepare` --- ## Layer 1 — DataSpace-Level Access (MDAPI) Embed a `` element inside the `PermissionSet` XML. Deploy with MDAPI. ```xml Data cloud analyst access to the default dataspace false default ALL BY_POLICY ``` ### Element Rules | Element | Required | Valid Values | Purpose | |---|---|---|---| | `` | yes | parent element (plural) | Container for a single dataspace scope grant | | `` | yes | DataSpace API name (e.g. `default`) | Which DataSpace this grant is for | | `` | yes | `NONE`, `CONTROLLED_BY_PARENT`, `ALL` | Row-level data access within the DataSpace | | `` | yes | `BY_POLICY`, `ALL_IN_DATASPACE` | Object-level access. `BY_POLICY` defers to data governance policies. `ALL_IN_DATASPACE` is only allowed when `dataAccessLevel` is `CONTROLLED_BY_PARENT` | ### Common Mistakes - **Wrong parent name** — using `` instead of ``. Deployment fails silently or with cryptic errors. - **Wrong child name** — using `` instead of ``. - **Wrong enum values** — `ViewAllRows` / `Read` / `OWNER` / `EDIT` are not valid. Use `NONE`, `CONTROLLED_BY_PARENT`, or `ALL` for `dataAccessLevel`; use `BY_POLICY` or `ALL_IN_DATASPACE` for `objectAccessLevel`. See Element Rules table for allowed combinations. Deployment error `-379999659` means invalid enum. - **Multiple scopes in one element** — `` grants access to exactly one DataSpace. To grant access to multiple, add multiple `` blocks. ### Package Layout (Case A and Case B) A deployable bundle for Layer 1 always contains **both** files: ```text / package.xml permissionsets/.permissionset-meta.xml ``` `package.xml` (required — list every permission set being deployed in ``): ```xml Data_Cloud_Analyst PermissionSet 67.0 ``` Deploy: ```bash sf project deploy start --source-dir force-app/main/default/permissionsets/ --target-org ``` --- ## Layer 2 — Object-Level Access (Connect API) — Case C Only needed when `objectAccessLevel` is not `BY_POLICY`, or when governance policies do not cover the target objects. Grants are runtime — **no MDAPI deploy, no `package.xml`, no permission set XML**. The only artifact for a Case C task is a single `api-request.json` describing the Connect API call. ### Resolve the API version first Every Connect API `endpoint` in this layer contains an `/services/data/v/…` segment. **Do not hardcode `v67.0`.** Resolve the target org's actual API version before writing the envelope so the request matches the org's supported surface: ```bash sf org display --target-org --json | jq -r '.result.apiVersion' ``` - Substitute the returned value (e.g. `67.0`, `68.0`) into the `endpoint` as `v`. - If the org can't be queried (offline authoring, no alias yet), fall back to the `minApiVersion` from this skill's frontmatter (`67.0`) — the endpoint was introduced there and any newer version accepts the same body. - If the user explicitly specifies a version in the prompt, use that verbatim. In the templates below, `{apiVersion}` is a placeholder. Replace it with the resolved API version (e.g., `67.0`, `68.0`) before emitting `api-request.json`. ### `api-request.json` — canonical shape Emit the request as a self-describing envelope with `method`, `endpoint`, `headers`, `body`, and `expectedResponse`. Do NOT emit only the body — reviewers and downstream tooling read the envelope. ```json { "method": "POST", "endpoint": "/services/data/v{apiVersion}/ssot/data-governance/object-access-grants", "headers": { "Content-Type": "application/json" }, "body": { "permissionSetName": "Data_Cloud_Analyst", "dataSpaceName": "default", "objectApiName": "Account__dlm" }, "expectedResponse": { "status": 201, "body": { "permissionSetName": "Data_Cloud_Analyst", "dataSpaceName": "default", "objectApiName": "Account__dlm" } } } ``` ### Bulk Grant Same `api-request.json` envelope shape. `endpoint` gains the `/actions/bulk-create` suffix, `body.objectApiName` is replaced by the list-valued `body.objectApiNames`, and `expectedResponse` omits the `body` field because bulk responses return per-object status entries rather than the flat request payload (see Gotchas below). ```json { "method": "POST", "endpoint": "/services/data/v{apiVersion}/ssot/data-governance/object-access-grants/actions/bulk-create", "headers": { "Content-Type": "application/json" }, "body": { "permissionSetName": "Data_Cloud_Analyst", "dataSpaceName": "default", "objectApiNames": ["Account__dlm", "Contact__dlm", "Opportunity__dlm"] }, "expectedResponse": { "status": 201 } } ``` ### List Grants Same envelope shape with `method: "GET"`, query parameters on the `endpoint`, and no `body`. ```json { "method": "GET", "endpoint": "/services/data/v{apiVersion}/ssot/data-governance/object-access-grants?permissionSetName=Data_Cloud_Analyst&dataSpaceName=default", "headers": { "Accept": "application/json" }, "expectedResponse": { "status": 200 } } ``` ### Revoke Grant Same envelope shape with `method: "DELETE"`, the object API name as a path segment, and `expectedResponse.status: 204` (No Content). ```json { "method": "DELETE", "endpoint": "/services/data/v{apiVersion}/ssot/data-governance/object-access-grants/Account__dlm?permissionSetName=Data_Cloud_Analyst&dataSpaceName=default", "headers": { "Accept": "application/json" }, "expectedResponse": { "status": 204 } } ``` ### Object Types - **DMO** (Data Model Object) — unified profile objects, suffix `__dlm` - **DLO** (Data Lake Object) — raw ingested data, suffix `__dll` - **CIO** (Calculated Insight Object) — computed metrics, suffix `__cio` --- ## Combined Setup — Case A + Case C from a Cold Start Use this section ONLY when the user is starting from nothing and asks for both the permset+scope AND per-object grants in a single request. If the user's prompt is only about the Connect API grant (Case C) — for example "grant Account__dlm access; the permset and dataspace scope already exist" — SKIP this section entirely and emit only `api-request.json` from Layer 2. The commands below are operator-facing `sf` CLI invocations (a runnable cold-start walkthrough), NOT the artifact you emit. For a normal Case C task the artifact is a single `api-request.json` envelope as documented in Layer 2 above. **Goal:** Grant `Data_Cloud_Analyst` permission set access to `Account__dlm` and `Contact__dlm` in the `default` DataSpace. **Step 1 — Deploy PermissionSet with DataSpace scope (MDAPI):** ```xml Data cloud analyst access to the default dataspace false default ALL BY_POLICY ``` ```bash sf project deploy start --source-dir permissionsets/ --target-org ``` **Step 2 — Grant object access (Connect API):** *(Required here because the `default` DataSpace has no governance policies covering `Account__dlm` and `Contact__dlm`. Skip Step 2 when `BY_POLICY` policies already govern the target objects — Layer 1 alone is sufficient.)* Resolve the org's API version first (see [Resolve the API version first](#resolve-the-api-version-first) above), then substitute it into the `--path` value: ```bash API_VERSION=$(sf org display --target-org --json | jq -r '.result.apiVersion') sf org api rest --target-org \ --method POST \ --path "/services/data/v${API_VERSION}/ssot/data-governance/object-access-grants/actions/bulk-create" \ --body '{ "permissionSetName": "Data_Cloud_Analyst", "dataSpaceName": "default", "objectApiNames": ["Account__dlm", "Contact__dlm"] }' ``` **Step 3 — Verify:** ```bash sf org api rest --target-org \ --path "/services/data/v${API_VERSION}/ssot/data-governance/object-access-grants?permissionSetName=Data_Cloud_Analyst&dataSpaceName=default" ``` --- ## Rules and Constraints | Rule | Reason | |---|---| | Use `` (plural) as parent, `` (singular) as child | XML schema requirement; other names deploy-fail | | `dataAccessLevel` values: `NONE`, `CONTROLLED_BY_PARENT`, `ALL` only | Other values (e.g. `OWNER`, `ViewAllRows`) are rejected | | `objectAccessLevel` values: `BY_POLICY`, `ALL_IN_DATASPACE` only | Other values (e.g. `READ`, `EDIT`, `Read`) are rejected. `ALL_IN_DATASPACE` requires `dataAccessLevel=CONTROLLED_BY_PARENT` | | Prefer `BY_POLICY` when data governance policies exist | Delegates row/column filtering to central policy — no per-object grants needed | | One `` block per DataSpace | Repeat the block for multiple DataSpaces on the same permission set | | Org must have Data Cloud provisioned to deploy `` | On non-Data-Cloud orgs, the element is ignored or rejected | | Do not query `DataspaceScope` / `DataspaceScopeAccess` via SOQL | Not queryable; use MDAPI retrieve to inspect existing grants | --- ## Gotchas | Issue | Resolution | |---|---| | Deployment fails with error `-379999659` | Check enum values — `dataAccessLevel` must be `NONE`/`CONTROLLED_BY_PARENT`/`ALL`; `objectAccessLevel` must be `BY_POLICY`/`ALL_IN_DATASPACE` | | Permission set deploys but users still can't query DataSpace data | Layer 2 not applied — objects need explicit grants if `objectAccessLevel != BY_POLICY` | | Bulk-create returns `AlreadyExists` for some objects | Idempotent — safe to retry; response shows per-object status | | Connect API returns 404 for object grants endpoint | Org lacks Data Cloud provisioning, or the resolved API version is below the minimum. The endpoint was introduced in `v67.0` — re-run `sf org display --json` to confirm the org's `apiVersion` field is `67.0` or later, and substitute that value into the `endpoint` path | | Retrieved PermissionSet XML shows different element names than deployed | Metadata API sometimes echoes legacy names on retrieve — always author with current names |