Audience: auditors, data protection officers, and compliance reviewers. This page documents the Right to Erasure (“Right to be forgotten”, GDPR Article 17) implementation for an entire organisation tenant.

Purpose

When an organisation Admin requests deletion of their organisation, FocusFlow performs an irreversible, cascading erasure of the organisation and all of its associated records — including every member’s authentication account and personal data — and produces a tamper-evident audit record that the request was fulfilled. The control is self-service (initiated by the customer’s own Admin) and requires no operator intervention, satisfying the controller’s obligation to act on an erasure request without undue delay.
This operation is permanent and irreversible. There is no soft-delete, recycle bin, or recovery window. Once complete, the data cannot be restored.
AspectDetail
RegulationEU GDPR Article 17 — Right to erasure
TriggerOrganisation Admin action in Settings → Danger Zone
ScopeOne organisation tenant and every record linked to it
Authorisationadmin role only, scoped to the Admin’s own organisation
ReversibilityNone — hard delete
Evidence retainedMinimal audit record (see Audit evidence)

Who can initiate

Authorisation

Only a user with the admin role may initiate erasure. Enforced in both the UI (the control is hidden for non-admins) and server-side (the function returns 403 for non-admins).

Scope binding

The target organisation is derived from the caller’s own profile — an Admin cannot delete another organisation, even by tampering with the request.

Explicit confirmation

The Admin must type the exact organisation name to confirm. A mismatch is rejected with 400 before any data is touched.

No self-exception

The Admin’s own account is included in the erasure — this is whole-tenant deletion, not a transfer of ownership.

Data inventory

The following table is the authoritative inventory of what is stored against an organisation and how each item is handled during erasure.
Data storeContainsPersonal data?Action on erasure
organisationsOrg name, settings, Stripe identifiersIndirectDeleted (root of cascade)
profilesMember name, email, role, avatar URLYesDeleted (cascade)
devicesEnrolled device identifiers & metadataIndirectDeleted (cascade)
nfc_tagsNFC tag UIDs, locationsNoDeleted (cascade)
restriction_profilesApp/category restriction setsNoDeleted (cascade)
policiesPolicy rules and targetsNoDeleted (cascade)
scan_eventsScan history, optional geolocationYesDeleted (cascade)
auth.usersLogin credentials, email (PII)YesDeleted (explicit, per member)
Storage — organisation-logosOrg logo imageNoDeleted (explicit)
Storage — avatarsMember profile imagesYesDeleted (explicit, per member)
Stripe subscriptionBilling relationshipIndirectCancelled (best-effort)
organisation_deletion_logErasure evidenceMinimal (requester email)Created & retained

Cascade map

Database deletion is enforced at the schema level by ON DELETE CASCADE foreign keys. Deleting the single organisations row removes every dependent record atomically within the database:
organisations (DELETE)
├── profiles            ON DELETE CASCADE
├── devices             ON DELETE CASCADE
│   └── scan_events     ON DELETE CASCADE   (device-scoped scans)
├── nfc_tags            ON DELETE CASCADE
├── restriction_profiles ON DELETE CASCADE
├── policies            ON DELETE CASCADE
└── scan_events         ON DELETE CASCADE   (org-scoped scans)
auth.users records are not removed by the database cascade — they live in the authentication schema and are deleted explicitly per member by the erasure function. This is what removes login credentials and email PII.

The erasure process

The erasure is performed by a single server-side function (offboard) running with elevated privileges. Steps execute in a deliberate order so that the data subject is notified before their own contact details are destroyed.
1

Authenticate & authorise

The caller’s session is verified. Their profile is loaded and the admin role is confirmed. The target organisation is taken from the caller’s profile — not from client input.
2

Confirm intent

The submitted confirmation text must exactly match the organisation name. Any mismatch aborts the request before any data is modified.
3

Enumerate members

All member accounts in the organisation are listed so their auth records and avatars can be removed, and so the confirmation email can be addressed.
4

Send confirmation email — before erasure

A confirmation email is sent to the requesting Admin while personal data is still intact, because their own account and email are about to be deleted. This closes the loop on the erasure request. Email failure is logged but does not block the erasure.
5

Cancel subscription

Any active Stripe subscription is cancelled (best-effort). Failure is logged and does not block the erasure.
6

Remove stored files

The organisation logo and every member avatar are removed from object storage.
7

Delete the organisation (cascade)

The organisations row is deleted, cascading to all dependent tables (profiles, devices, nfc_tags, restriction_profiles, policies, scan_events).
8

Delete authentication accounts

Each member’s auth.users record is deleted, erasing login credentials and email PII. Per-user outcomes are tracked.
9

Write audit evidence

An immutable audit record is written to organisation_deletion_log capturing the outcome (see below). This record has no foreign keys and therefore survives the cascade.

Processing order rationale

The confirmation email is intentionally sent before the destructive steps. Once a member’s auth.users record and profiles row are deleted, their email address no longer exists in the system and cannot be contacted. Sending first guarantees the data subject receives closure evidence.

Notification to the data subject

A transactional email is generated and sent via Resend to the requesting Admin. It confirms the erasure and itemises what was removed.
FieldValue
RecipientRequesting Admin’s email
ChannelResend transactional API
TimingBefore any personal data is deleted
ContentsOrganisation name, member count removed, completion timestamp, list of erased data categories, retention notice
By default only the initiating Admin is notified. Notifying every member individually is supported by the same mechanism but is not enabled by default.

Audit evidence

To evidence that the erasure request was fulfilled, a single record is written to the organisation_deletion_log table. This table is deliberately designed to outlive the deleted organisation:
  • It holds no foreign keys, so the cascade cannot remove it.
  • It is protected by Row Level Security with no access policies, so it is reachable only by the privileged server-side process — never by API clients, members, or admins.
  • It retains the minimum personal data necessary to evidence the request: the requester’s email.
ColumnPurpose
idUnique record identifier
organisation_idThe erased organisation (no longer resolvable)
organisation_nameHuman-readable identification of the tenant
requested_byThe Admin’s former user id
requested_by_emailWho exercised the right (evidence)
member_countNumber of member accounts in scope
deleted_user_countNumber of auth accounts actually deleted
notification_sentWhether the confirmation email succeeded
notification_emailAddress the confirmation was sent to
statuscompleted or completed_with_errors
created_atTimestamp of completion
The audit log stores no restriction data, scan history, device data, or member records — only the minimal evidence of the erasure event itself, consistent with data minimisation.

Failure handling & integrity

Sub-stepOn failure
Authorisation / confirmationRequest aborted; no data modified
Confirmation emailLogged; erasure continues; notification_sent = false recorded
Stripe cancellationLogged; erasure continues
Storage cleanupLogged; erasure continues
Organisation deleteRequest fails with 500; cascade is atomic (all-or-nothing)
Per-member auth deleteFailures recorded in deleted_user_count and status
Audit log writeLogged; request still reports success (data already erased)
The database cascade is atomic — the organisation and all dependent rows are removed in a single transaction, so the relational data cannot be left in a partially-deleted state.

Residual data & third parties

Backups

Personal data may persist in encrypted database backups for up to 30 days, after which the backups age out and the data is purged. Backups are not used to restore individual tenants.

Stripe

The subscription is cancelled. Billing/transaction records retained by Stripe are governed by Stripe’s own retention and legal/tax obligations.

Email provider

The confirmation email transits Resend and may appear in their delivery logs per their retention policy.

Observability

Operational logs reference identifiers (e.g. user ids) but are not the system of record and age out per the logging retention policy.

Summary for reviewers

Erasure is self-service, Admin-only, and scoped to the caller’s own tenant.
Explicit name-match confirmation is required before any change.
All organisation-linked records are removed via atomic database cascade.
Authentication accounts (credentials + email PII) are explicitly deleted.
Stored files (logos, avatars) are removed; subscription is cancelled.
The data subject is notified before their contact details are destroyed.
A minimal, access-restricted audit record evidences fulfilment of the request.