Implement GDPR/CCPA data subject rights (access, deletion, rectification) using Salesforce Privacy Center and/or custom workflow. NOT for the underlying erasure mechanics (Individual sObject, ShouldForget, ContactPointConsent) — use security/gdpr-data-privacy. NOT for general backup or org-level data retention policy.
A data subject request is a regulatory workflow with a deadline, a scope that spans
more objects than anyone expects, and an evidentiary requirement: you must be able to
show a regulator what you did, when, and to which records.
Scope of this skill. This is the workflow: intake, scoping, orchestration,
evidence, rehearsal, and the operational commitments that follow. The underlying
erasure mechanics — the Individual sObject's full field model, ShouldForget
semantics in depth, ContactPointConsent and the consent object family — belong to
security/gdpr-data-privacy. They appear here only where they change a workflow
decision.
Three building blocks, and which you use changes the whole design:
Building block
What it is
Individual
Stores the subject's privacy preferences. Available "if Data Protection and Privacy is enabled," and not available to Customer Community, Partner Community, or Customer Portal users.
Privacy Center
Managed policies that "anonymize or transfer personal data from your org at your customer's request," surfaced as DsarPolicy and DsarPolicyLog
Custom workflow
Policy-as-Custom-Metadata plus batch or Queueable Apex, for orgs without Privacy Center
Before Starting
Separate access from erasure. They differ in scope, direction, output, SLA,
approval gate, and risk. Model them as two workflows sharing an Individual
record, not one handler with a mode flag.
Confirm Data Protection and Privacy is enabled if the design uses
Individual, and plan a staging object if requests can arrive from a portal —
community users cannot see the object.
Get the legal positions before you build: deletion versus pseudonymisation,
what cannot be erased and under which competing obligation, and the applicable
deadline per jurisdiction. These are legal artifacts engineering cannot author.
Name the owner of every off-platform copy: sandboxes, backups, CRM Analytics
datasets, warehouse extracts, marketing platform. Each needs a stated position in
the runbook.
Core Concepts
Preferences are not actions
Individual.ShouldForget is the "Preference to delete records and personal data
related to this customer." SendIndividualData is the "Preference to export personal
data for delivery to the customer." Both are stored intentions. Their value is as the
anchor: they record that the request was made, start the SLA clock, and give
downstream jobs a queryable population.
Privacy Center: policies are inactive by default
DsarPolicy.IsActive — "Indicates whether this policy can be used (true) or not
(false) for data subject (customer) requests. The default value is false." Access
requires ReadAllData or PrivacyDataAccess.
DsarPolicyLog is the evidence, generated as a side effect of execution so it cannot
drift from what was done. It carries the requesting subject's ID, the request and
completion timestamps, the generated file's deletion and download timestamps, an error
field, and the ID of the employee or admin who acted on the subject's behalf.
The object inventory is the hard part
Build the relational half from Schema.DescribeSObjectResult.getChildRelationships().
Then add what describe cannot reach:
"If you delete a record in your production data, the delete cascades to the related
history tracking records, but Salesforce doesn't delete the history copied into the
FieldHistoryArchive big object."
In a Shield org this leaves the old values of the erased fields in a separate store.
It is the most commonly missed step in this domain and the easiest to prove missing.
Deleted is not deleted
A standard delete is a soft delete. Follow with Database.emptyRecycleBin or a Bulk
API hard delete, and verify.
The audit must not become a second copy
Log a SHA-256 hash of the original value, never the value. Write the audit row
before the DML so a failure mid-run leaves evidence of the attempt.
Common Patterns
Pattern A — Privacy Center
Author object-scoped policies, activate them as a distinct verified step, and use
DsarPolicyLog as the compliance report. Completeness of the erasure equals
completeness of the object inventory. Example 2 in
references/examples.md.
Pattern B — policy-as-metadata plus Queueable
DSR_Policy__mdt rows (object, field, action, relationship path, justification) read
via getAll(); a Queueable walks them, writes hashed audit rows before each DML, and
executes NULL / REDACT / DELETE per row. Compliance changes scope without an Apex
deploy. Example 4.
Pattern C — pseudonymisation for Person Accounts
Null or redact identifying fields, keep the record and its relationships. Preserves
referential integrity across Orders, Cases, and Assets. Record the choice as a legal
position.
Pattern D — sandbox rehearsal against a synthetic subject
Create a subject with data in every object on the inventory including the awkward ones
— a Chatter post, an email message, a file, a portal User, a consent record, a
history-tracked field. Run the workflow. Then search as an unrestricted admin for
residuals, query FieldHistoryArchive, and check the Recycle Bin. Record elapsed time
per object. Example 5.
Decision Guidance
Situation
Approach
Org has Privacy Center
DsarPolicy + DsarPolicyLog; verify IsActive
No Privacy Center licence
Policy-as-Custom-Metadata + Queueable, with a hashed audit object
Subject is a Person Account
Pseudonymise; deleting cascades into transactional records
Subject has portal/Experience Cloud logins
The User record is in scope, and the portal cannot see Individual — stage the request
Org has Field Audit Trail
FieldHistoryArchive deletion is a mandatory separate step
Record retained under a competing legal obligation
Do not erase; add it to the residual-risk register with a written legal position
Sandboxes hold copies
Masking on refresh as the standing control; repeat erasure only for existing copies
Access / portability request
Read-only workflow; the generated package is itself sensitive and has its own retention
Volume spike after an incident
Measure per-request timings first; the deadline is per request under load
Recommended Workflow
Intake and anchor. Record the request against the subject — Individual
where available, a staging object for portal-submitted requests — and start the
SLA clock from that record.
Regenerate the object inventory from the schema for this request rather than
reusing a list, then add the free-text, archive, and off-platform checklist items.
Classify each object: NULL, REDACT, DELETE, or retain-under-obligation. The
retain decisions go to the residual-risk register with a legal position.
Execute with evidence: write the hashed audit row before each DML, run in
system mode with a tightly scoped permission set and an approval gate, and make the
run idempotent so a partial failure can be re-run.
Complete the erasure: hard delete from the Recycle Bin, delete from
FieldHistoryArchive, and execute the stated position for every off-platform copy.
Verify and evidence: search as an unrestricted admin for residuals across every
inventory item and in global search, then produce the completion record —
DsarPolicyLog or the custom audit — and confirm to the requester.
Rehearse quarterly in a full sandbox against a synthetic subject, recording
elapsed time per object so the process can be sized against post-incident volume.
Review Checklist
Access and erasure are separate workflows with separate approval gates
Request anchored on a record that starts the SLA clock
Object inventory regenerated from the schema, not reused
Consent, free-text, file, and history objects present in the inventory
FieldHistoryArchive deletion is an explicit step
Hard delete follows every soft delete, and is verified
Audit rows written before DML, and store a hash rather than the value
Person Accounts pseudonymised, with the legal position recorded
Portal-submitted requests use a staging object, not Individual directly
Privacy Center policies confirmed IsActive = true
Every off-platform copy has a named owner and a stated position
Residual-risk register lists what cannot be erased and why, with legal sign-off
Sandbox rehearsal completed against a synthetic subject, with residual search
Per-object elapsed times measured and sized against post-incident volume
Salesforce-Specific Gotchas
Full detail with quotes in references/gotchas.md.
ShouldForget is a preference, not an action.
The Individual object is off by default and invisible to portal users.
A Privacy Center policy defaults to inactive.
Deleting the record does not delete the archived field history.
Deleted is not deleted until it leaves the Recycle Bin.
Person Accounts are two objects wearing one record.
Free-text fields have no relationship to traverse.
Sandboxes hold a full copy of everything you just erased.
The audit trail can become a second copy of the data.
Some records cannot be deleted, and that needs a written legal position.
The deadline is measured per request, under load.
Output Artifacts
Artifact
Description
Object inventory
Regenerated per request: relational half from describe, plus the free-text, consent, archive, and off-platform checklist, each classified NULL / REDACT / DELETE / retain
Erasure runbook
Ordered steps including hard delete, FieldHistoryArchive, and the stated position for every off-platform copy, with an owner per step
Evidence record
DsarPolicyLog or a hashed custom audit: object, record Id, field, action, timestamp, hash — and never the value
Residual-risk register
What cannot be erased, under which competing obligation, with the compensating control and legal sign-off
Rehearsal report
Synthetic-subject run in a full sandbox: residuals found, archive query result, Recycle Bin check, and elapsed time per object
Completion confirmation
What was sent to the requester and when, retained per the org's own retention policy
Related Skills
security/gdpr-data-privacy — the erasure mechanics this workflow orchestrates:
the Individual object in depth, ShouldForget semantics, and the consent object
family
security/field-audit-trail — the FieldHistoryArchive store and its separate
deletion procedure
security/sandbox-data-masking — the standing control that makes sandbox copies a
documented position rather than a residual exposure
security/data-classification-labels — the classification that tells you which
fields are in scope before a request arrives
1---2name: customer-data-request-workflow3description: Implement GDPR/CCPA data subject rights (access, deletion, rectification) using Salesforce Privacy Center and/or custom workflow. NOT for the underlying erasure mechanics (Individual sObject, ShouldForget, ContactPointConsent) — use security/gdpr-data-privacy. NOT for general backup or org-level data retention policy.4---56# Customer Data Subject Request (DSR) Workflow78A data subject request is a regulatory workflow with a deadline, a scope that spans9more objects than anyone expects, and an evidentiary requirement: you must be able to10show a regulator *what* you did, *when*, and *to which records*.1112**Scope of this skill.** This is the *workflow*: intake, scoping, orchestration,13evidence, rehearsal, and the operational commitments that follow. The underlying14erasure mechanics — the `Individual` sObject's full field model, `ShouldForget`15semantics in depth, `ContactPointConsent` and the consent object family — belong to16`security/gdpr-data-privacy`. They appear here only where they change a workflow17decision.1819Three building blocks, and which you use changes the whole design:2021| Building block | What it is |22|---|---|23| **Individual** | Stores the subject's privacy preferences. Available "if Data Protection and Privacy is enabled," and not available to Customer Community, Partner Community, or Customer Portal users. |24| **Privacy Center** | Managed policies that "anonymize or transfer personal data from your org at your customer's request," surfaced as `DsarPolicy` and `DsarPolicyLog` |25| **Custom workflow** | Policy-as-Custom-Metadata plus batch or Queueable Apex, for orgs without Privacy Center |2627---2829## Before Starting30311. **Separate access from erasure.** They differ in scope, direction, output, SLA,32 approval gate, and risk. Model them as two workflows sharing an `Individual`33 record, not one handler with a mode flag.34352. **Confirm Data Protection and Privacy is enabled** if the design uses36 `Individual`, and plan a staging object if requests can arrive from a portal —37 community users cannot see the object.38393. **Get the legal positions before you build**: deletion versus pseudonymisation,40 what cannot be erased and under which competing obligation, and the applicable41 deadline per jurisdiction. These are legal artifacts engineering cannot author.42434. **Name the owner of every off-platform copy**: sandboxes, backups, CRM Analytics44 datasets, warehouse extracts, marketing platform. Each needs a stated position in45 the runbook.4647---4849## Core Concepts5051### Preferences are not actions5253`Individual.ShouldForget` is the "Preference to delete records and personal data54related to this customer." `SendIndividualData` is the "Preference to export personal55data for delivery to the customer." Both are stored intentions. Their value is as the56**anchor**: they record that the request was made, start the SLA clock, and give57downstream jobs a queryable population.5859### Privacy Center: policies are inactive by default6061`DsarPolicy.IsActive` — "Indicates whether this policy can be used (`true`) or not62(`false`) for data subject (customer) requests. The default value is `false`." Access63requires `ReadAllData` or `PrivacyDataAccess`.6465`DsarPolicyLog` is the evidence, generated as a side effect of execution so it cannot66drift from what was done. It carries the requesting subject's ID, the request and67completion timestamps, the generated file's deletion and download timestamps, an error68field, and the ID of the employee or admin who acted on the subject's behalf.6970### The object inventory is the hard part7172Build the relational half from `Schema.DescribeSObjectResult.getChildRelationships()`.73Then add what describe cannot reach:7475```text76IDENTITY portal/Experience Cloud Users; Person Accounts (Account AND Contact)77CONSENT ContactPointEmail/Phone/Address, ContactPointConsent,78 ContactPointTypeConsent, AuthorizationFormConsent,79 CommunicationSubscriptionConsent, PartyConsent80FREE TEXT Case Comments, Chatter, Email Messages, Notes, Attachments,81 ContentDocument/ContentVersion bodies, long-text fields82ARCHIVE <Object>History, FieldHistoryArchive, Setup Audit Trail83OFF-PLATFORM backups, sandboxes, CRM Analytics, warehouse, marketing platform84```8586### The archive does not cascade8788> "If you delete a record in your production data, the delete cascades to the related89> history tracking records, but Salesforce doesn't delete the history copied into the90> `FieldHistoryArchive` big object."9192In a Shield org this leaves the old values of the erased fields in a separate store.93It is the most commonly missed step in this domain and the easiest to prove missing.9495### Deleted is not deleted9697A standard `delete` is a soft delete. Follow with `Database.emptyRecycleBin` or a Bulk98API hard delete, and verify.99100### The audit must not become a second copy101102Log a SHA-256 hash of the original value, never the value. Write the audit row103**before** the DML so a failure mid-run leaves evidence of the attempt.104105---106107## Common Patterns108109### Pattern A — Privacy Center110111Author object-scoped policies, **activate** them as a distinct verified step, and use112`DsarPolicyLog` as the compliance report. Completeness of the erasure equals113completeness of the object inventory. Example 2 in114[`references/examples.md`](references/examples.md).115116### Pattern B — policy-as-metadata plus Queueable117118`DSR_Policy__mdt` rows (object, field, action, relationship path, justification) read119via `getAll()`; a Queueable walks them, writes hashed audit rows before each DML, and120executes NULL / REDACT / DELETE per row. Compliance changes scope without an Apex121deploy. Example 4.122123### Pattern C — pseudonymisation for Person Accounts124125Null or redact identifying fields, keep the record and its relationships. Preserves126referential integrity across Orders, Cases, and Assets. Record the choice as a legal127position.128129### Pattern D — sandbox rehearsal against a synthetic subject130131Create a subject with data in every object on the inventory including the awkward ones132— a Chatter post, an email message, a file, a portal User, a consent record, a133history-tracked field. Run the workflow. Then search as an unrestricted admin for134residuals, query `FieldHistoryArchive`, and check the Recycle Bin. Record elapsed time135per object. Example 5.136137---138139## Decision Guidance140141| Situation | Approach |142|---|---|143| Org has Privacy Center | `DsarPolicy` + `DsarPolicyLog`; verify `IsActive` |144| No Privacy Center licence | Policy-as-Custom-Metadata + Queueable, with a hashed audit object |145| Subject is a Person Account | Pseudonymise; deleting cascades into transactional records |146| Subject has portal/Experience Cloud logins | The User record is in scope, and the portal cannot see `Individual` — stage the request |147| Org has Field Audit Trail | `FieldHistoryArchive` deletion is a mandatory separate step |148| Record retained under a competing legal obligation | Do not erase; add it to the residual-risk register with a written legal position |149| Sandboxes hold copies | Masking on refresh as the standing control; repeat erasure only for existing copies |150| Access / portability request | Read-only workflow; the generated package is itself sensitive and has its own retention |151| Volume spike after an incident | Measure per-request timings first; the deadline is per request under load |152153---154155## Recommended Workflow1561571. **Intake and anchor.** Record the request against the subject — `Individual`158 where available, a staging object for portal-submitted requests — and start the159 SLA clock from that record.1602. **Regenerate the object inventory** from the schema for this request rather than161 reusing a list, then add the free-text, archive, and off-platform checklist items.1623. **Classify each object**: NULL, REDACT, DELETE, or retain-under-obligation. The163 retain decisions go to the residual-risk register with a legal position.1644. **Execute with evidence**: write the hashed audit row before each DML, run in165 system mode with a tightly scoped permission set and an approval gate, and make the166 run idempotent so a partial failure can be re-run.1675. **Complete the erasure**: hard delete from the Recycle Bin, delete from168 `FieldHistoryArchive`, and execute the stated position for every off-platform copy.1696. **Verify and evidence**: search as an unrestricted admin for residuals across every170 inventory item and in global search, then produce the completion record —171 `DsarPolicyLog` or the custom audit — and confirm to the requester.1727. **Rehearse quarterly in a full sandbox** against a synthetic subject, recording173 elapsed time per object so the process can be sized against post-incident volume.174175---176177## Review Checklist178179- [ ] Access and erasure are separate workflows with separate approval gates180- [ ] Request anchored on a record that starts the SLA clock181- [ ] Object inventory regenerated from the schema, not reused182- [ ] Consent, free-text, file, and history objects present in the inventory183- [ ] `FieldHistoryArchive` deletion is an explicit step184- [ ] Hard delete follows every soft delete, and is verified185- [ ] Audit rows written **before** DML, and store a hash rather than the value186- [ ] Person Accounts pseudonymised, with the legal position recorded187- [ ] Portal-submitted requests use a staging object, not `Individual` directly188- [ ] Privacy Center policies confirmed `IsActive = true`189- [ ] Every off-platform copy has a named owner and a stated position190- [ ] Residual-risk register lists what cannot be erased and why, with legal sign-off191- [ ] Sandbox rehearsal completed against a synthetic subject, with residual search192- [ ] Per-object elapsed times measured and sized against post-incident volume193194---195196## Salesforce-Specific Gotchas197198Full detail with quotes in [`references/gotchas.md`](references/gotchas.md).1992001. **`ShouldForget` is a preference, not an action.**2012. **The `Individual` object is off by default and invisible to portal users.**2023. **A Privacy Center policy defaults to inactive.**2034. **Deleting the record does not delete the archived field history.**2045. **Deleted is not deleted until it leaves the Recycle Bin.**2056. **Person Accounts are two objects wearing one record.**2067. **Free-text fields have no relationship to traverse.**2078. **Sandboxes hold a full copy of everything you just erased.**2089. **The audit trail can become a second copy of the data.**20910. **Some records cannot be deleted**, and that needs a written legal position.21011. **The deadline is measured per request, under load.**211212---213214## Output Artifacts215216| Artifact | Description |217|---|---|218| Object inventory | Regenerated per request: relational half from describe, plus the free-text, consent, archive, and off-platform checklist, each classified NULL / REDACT / DELETE / retain |219| Erasure runbook | Ordered steps including hard delete, `FieldHistoryArchive`, and the stated position for every off-platform copy, with an owner per step |220| Evidence record | `DsarPolicyLog` or a hashed custom audit: object, record Id, field, action, timestamp, hash — and never the value |221| Residual-risk register | What cannot be erased, under which competing obligation, with the compensating control and legal sign-off |222| Rehearsal report | Synthetic-subject run in a full sandbox: residuals found, archive query result, Recycle Bin check, and elapsed time per object |223| Completion confirmation | What was sent to the requester and when, retained per the org's own retention policy |224225---226227## Related Skills228229- `security/gdpr-data-privacy` — the erasure mechanics this workflow orchestrates:230 the `Individual` object in depth, `ShouldForget` semantics, and the consent object231 family232- `security/field-audit-trail` — the `FieldHistoryArchive` store and its separate233 deletion procedure234- `security/sandbox-data-masking` — the standing control that makes sandbox copies a235 documented position rather than a residual exposure236- `security/data-classification-labels` — the classification that tells you which237 fields are in scope before a request arrives
Run npx skillmds add pranavnagrecha/customer-data-request-workflow in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Implement GDPR/CCPA data subject rights (access, deletion, rectification) using Salesforce Privacy Center and/or custom workflow. NOT for the underlying erasure mechanics (Individual sObject, ShouldForget, ContactPointConsent) — use security/gdpr-data-privacy. NOT for general backup or org-level data retention policy. It is listed under Security on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Capability flags: executes scripts. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
PranavNagrecha (@pranavnagrecha) published this skill. Their other Agent Skills are listed on their SkillMD profile.