Skip to Content
GovernanceDeletion Rules

Deletion Rules

A deletion rule erases data for a population you define, across your warehouse, on demand or on a schedule — and records exactly what it did. Use it to fulfil GDPR right-to-erasure and CCPA deletion requests at scale, instead of looking up and deleting identifiers one at a time.

A live deletion permanently removes data from your warehouse. It is not recoverable through Zeotap. Every rule starts in observe mode and disabled, so you can see what it would delete before it deletes anything.

How a Deletion Rule Works

A rule has four parts:

FieldDescription
Parent modelThe entity the rule operates on — usually your person or user model. It defines the grain of the deletion and constrains which models can be targeted.
Match conditionsThe records to erase, expressed as a filter tree in the same language as an audience. A live estimate shows how many records currently match.
TargetsOne or more models, each with either delete the whole row or clear specific columns.
ScheduleManual, or a recurrence. Each run resolves the match conditions again, so newly matching records are erased too.
A deletion rule run: match, freeze the matched set, count and delete per target, then log

Each run resolves the matched population once and writes the matched keys to a short-lived table in your warehouse’s planner schema (CDP_PLANNER), which every target then reads. Two things follow from that, and both matter:

  • The numbers in the log are the numbers that happened. The count reported for a target and the statement executed against it select the same rows, so records affected is never an estimate.
  • All targets see one population. Without this, data changing mid-run could leave a person’s rows deleted from one table and still present in another — the one outcome an erasure request cannot tolerate.

The table is dropped when the run finishes.

Choosing What Gets Deleted

For each target, pick one:

ActionEffect
Delete the whole rowA physical DELETE. The record is gone from that table.
Clear specific columnsAn UPDATE setting the columns you choose to NULL. The row survives — useful when it must stay for referential or analytical reasons but the personal data on it must not.

Clearing columns is idempotent: the statement only matches rows where at least one selected column still holds a value, so a second run affects nothing and records affected always means “records that still held data”.

Which models can be targeted

A model appears in the target list only when all of the following hold. Anything ineligible is not offered rather than failing when you save.

RequirementWhy
On the same warehouse as the parent modelDeleting across warehouses would mean moving keys between them, which this feature does not do. Create one rule per warehouse.
Maps directly onto one warehouse tableA model built from custom SQL may filter or aggregate what its table holds, so deleting from the table would erase more than the model’s name describes.
The parent model itself, or directly related to itThe join comes from your declared relationships, so a rule can only use joins the rest of the platform already recognises. Either direction works.
Each of its rows belongs to a single recordA model reached through a many-to-one relationship — households from customers, say — shares one row across many records. Deleting it for one matched customer would erase the address of everyone else in that household, and the run would report “1 record affected” and look correct. Only one_to_many (parent → target), many_to_one (target → parent) and one_to_one qualify.
Not related through a join modelA many-to-many relationship gives the target no single column linking it to the matched population.

The join a rule is saved against is pinned to it. If you later delete or change that relationship, the next run refuses rather than silently matching on a different column — re-open the rule, review the new join, and save it again.

Columns that cannot be cleared

The column carrying the link to the parent model, and the model’s own primary key, are never offered. Clearing either would make the row unreachable on any later run and break the guarantee that re-running is safe.

Observe Mode, Enabling, and Running

A rule has one Run mode with three settings:

Run modeEffect
OffThe rule does not run. Its schedule is ignored and a live run is refused. You can still start a dry run by hand.
Observe onlyEvery run — manual and scheduled — reports what it would delete and changes nothing.
Live deletionRuns permanently erase matching data, including on the schedule, unattended.

The Dry run button is always available and always non-destructive, whatever the rule’s mode.

New rules are created in Observe only. Switching to Live deletion asks you to type the rule’s name back, and tells you what the schedule will do and when the rule was last dry-run.

Over the API these three settings are the dry_run and enabled booleans: Off is enabled: false, Observe only is dry_run: true, enabled: true, and Live deletion is dry_run: false, enabled: true.

A scheduled rule left in observe mode is a useful thing to keep: it reports how many records would be erased on every tick, so you can watch a policy’s volume before switching it live.

Availability

Deletion Rules are off in every workspace by default. A Zeotap platform administrator enables the feature per workspace; until they do, the section does not appear in the nav and the API answers 403 deletion rules are not enabled for this workspace.

Once a workspace is enabled, the permissions below take over — your own owners and admins build and run rules, without further involvement from the platform team. Disabling is immediate: the API starts refusing and any scheduled rule stops firing on the next tick.

Ask your Zeotap contact to enable Deletion Rules for a workspace. The grant and every later change is recorded in the platform audit log.

Permissions

PermissionGrants
deletion_rules.readView rules and deletion logs. Granted to Owner, Admin, and Member.
deletion_rules.writeCreate, edit, and delete rules. Owner and Admin.
deletion_rules.executeTrigger runs. Owner and Admin.

Write and execute are separate so you can let someone run an already-reviewed rule without letting them change what it deletes.

These permissions only apply in a workspace where the feature has been enabled — see Availability above. A workspace settings change cannot grant the feature to itself.

Deletion Logs

Every run writes a log entry, dry or live. Each entry records:

  • Rule name, trigger (manual or schedule), mode (dry run or live), and status
  • Records matched, and records affected in total
  • Who initiated it, and when it started and finished
  • Per target: the physical table, the action, the columns, the records affected, and the exact statement executed — or, in a dry run, the statement that would have been

Logs are retained even if the rule is deleted. Each entry snapshots the rule’s name, definition, and the initiating user’s email, so the audit trail stays readable and attributable after the rule (or the account) is gone. There is no way to delete a log entry.

Run statuses

StatusMeaning
runningIn progress. A run continues past the request that started it — refresh to follow it.
completedEvery target succeeded.
partialSome targets succeeded and some failed. Targets are independent statements, so this is a real state, not a flavour of failure — read the per-target errors and re-run.
failedNothing was erased.

What a Deletion Rule Does Not Do

This is the part worth reading before a compliance deadline.

Not coveredWhat to do instead
Derived and aggregated dataNothing cascades. The rule builder lists the related models, computed attributes, audiences, and syncs that read the targeted data. Act on those separately — for example, re-evaluate a computed attribute whose stored value now describes deleted data.
Records already sent to a destinationA deletion rule erases warehouse data. It does not recall records a sync already delivered. Use the destination’s own deletion API or its data-subject-request process.
Cross-warehouse deletionOne rule deletes within one warehouse. Create a rule per warehouse.
Two-person approvalThere is no dual-authorization workflow. Splitting deletion_rules.write from deletion_rules.execute is the available control.
Stopping a runA run cannot be cancelled once started. Dry-run first.
Shared rowsA model whose rows serve several records (a household, a company, a device shared by a family) cannot be targeted at all — see the eligibility table above.
Restoring deleted dataRecovery is your warehouse’s problem — Snowflake Time Travel, BigQuery table snapshots, Delta time travel. Confirm your retention window before running a rule live.

Creating a Rule via the UI

  1. Navigate to Governance → Deletion Rules
  2. Click Create rule
  3. Select the parent model the rule operates on
  4. Build the match conditions. The estimate panel shows how many records match — check it before continuing
  5. Choose what to delete for each target model. Review the “Also affected” warning listing what else reads this data
  6. Set a schedule, or leave it manual
  7. Name the rule and review the summary

The rule is created in Observe only. Open it, click Dry run, and read the log — each target shows the records it would affect and the exact statement it would issue. When those look right, set Run mode to Live deletion and use Run deletion.

Creating a Rule via the API

Rules are workspace-scoped. See Base URL and Authentication for the required headers.

# Erase opted-out EU customers: delete their user row, clear the address on orders curl -X POST "$API_BASE_URL/api/v1/workspaces/$WORKSPACE_ID/deletion-rules" \ -H "Authorization: Bearer $API_TOKEN" \ -H "X-Workspace-ID: $WORKSPACE_ID" \ -H "Content-Type: application/json" \ -d '{ "parent_model_id": "'"$USERS_MODEL_ID"'", "name": "Erase opted-out EU customers", "description": "Fulfils standing right-to-erasure requests for EU opt-outs.", "filter_tree": { "type": "and", "children": [ { "type": "condition", "condition_type": "property", "field": "region", "operator": "equals", "value": "EU" }, { "type": "condition", "condition_type": "property", "field": "erasure_requested", "operator": "equals", "value": true } ] }, "targets": [ { "model_id": "'"$USERS_MODEL_ID"'", "action": "delete_rows" }, { "model_id": "'"$ORDERS_MODEL_ID"'", "action": "null_columns", "columns": ["shipping_address", "phone"] } ], "schedule": "0 3 * * *", "dry_run": true, "enabled": false }'

dry_run and enabled default to true and false respectively, so omitting them creates an observe-only rule. Sending "dry_run": false with "enabled": true arms the rule immediately — do that deliberately.

Trigger a run:

# Non-destructive, whatever the rule's mode. Returns 202 with the opened run. curl -X POST "$API_BASE_URL/api/v1/workspaces/$WORKSPACE_ID/deletion-rules/$RULE_ID/dry-run" \ -H "Authorization: Bearer $API_TOKEN" \ -H "X-Workspace-ID: $WORKSPACE_ID" # Live. Requires enabled=true and dry_run=false on the rule. curl -X POST "$API_BASE_URL/api/v1/workspaces/$WORKSPACE_ID/deletion-rules/$RULE_ID/run" \ -H "Authorization: Bearer $API_TOKEN" \ -H "X-Workspace-ID: $WORKSPACE_ID"

Both return 202 Accepted with the run in running state — the deletion continues past the request. Poll the log to follow it:

curl "$API_BASE_URL/api/v1/workspaces/$WORKSPACE_ID/deletion-rules/$RULE_ID/runs" \ -H "Authorization: Bearer $API_TOKEN" \ -H "X-Workspace-ID: $WORKSPACE_ID"

See Governance API for the full endpoint list.

Warehouse Support

Deletion rules work on Snowflake, BigQuery, Databricks, and ClickHouse. On ClickHouse the deletion is issued as a synchronous mutation (ALTER TABLE … DELETE WHERE), because MergeTree tables have no standalone DELETE — the run does not report success until the rows are gone, though background merges continue afterwards.

Deletion needs write access to the target tables and to the CDP_PLANNER schema. That is the same schema syncs and computed attributes already use, so an existing warehouse connection typically needs no additional grants beyond DELETE / UPDATE on the tables you intend to target.

NeedFeature
Stop data reaching a destinationDestination Policies
Hide rows from specific usersAccess Policies
Mask sensitive columns in the UI and in syncsPII Masking
Control who can do any of thisRBAC
Last updated on