Gdpr Data Subject Requests

Production engineering guide for gdpr data subject requests covering patterns, implementation strategies, and operational best practices.

Orientation GDPR Data Subject Requests (DSARs) are formal exercises of rights under Articles 15–22 of the EU General Data Protection Regulation. From an engineering perspective, a DSAR requires your system to locate all personal data belonging to a named individual, compile it into a structured format, and either export it or erase it within the statutory 30‑day (extendable to 90‑day) window. This page matters when your platform stores user‑identifiable data and you need to operationalize the procedural steps—querying databases, invoking APIs, validating completeness, and producing an audit record—without violating the right to prompt handling or introducing data leakage.

Locating Personal Data for a Request

The first engineering step is to narrow the search scope to the specific data subject. The method depends on how identifiers are stored, but the mechanics are always the same: fetch the subject’s unique key, then scan every data store that might contain personal fields.

Querying a relational store

psql -h db.internal -U app -d prod -c \
"SELECT table_name, array_agg(column_name) AS personal_cols 
 FROM information_schema.columns 
 WHERE table_schema = 'public' 
 GROUP BY table_name;"

Filter the resulting list against known personal‑data columns (email, address, ID document numbers, etc.) and build a per‑table WHERE clause keyed on the subject identifier.

Pulling data through an internal API

curl -s -X GET "$API_ENDPOINT/subjects/$SUBJECT_ID/data \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json" | jq .

The jq filter can then prune fields that are not within the request scope (e.g., exclude system‑generated audit logs that are not “personal data” under the regulation).

Handling pseudonymised or hashed fields If your system uses salted hashes or tokenization, the DSAR workflow must include a de‑pseudonymization step keyed to a trusted key‑management service. Without that key, the data is effectively unreachable and the request must be logged as “data not locatable”—a common audit‑trail entry.

Compiling the Response

Once the relevant rows are identified, the next goal is to assemble them into a machine‑readable or human‑readable package that satisfies the Article 15 data‑portability format.

Building an export archive

mkdir -p dsar_export/$SUBJECT_ID
for table in $(psql -h db.internal -U app -d prod -t -A -c "SELECT table_name FROM ..."); do
  psql -h db.internal -U app -d prod -c \
    "\COPY (SELECT * FROM $table WHERE subject_id = '$SUBJECT_ID') TO '/tmp/$table.csv' WITH (FORMAT csv, HEADER true)"
  mv /tmp/$table.csv dsar_export/$SUBJECT_ID/
done
tar -czf dsar_export/$SUBJECT_ID.tar.gz -C . dsar_export/$SUBJECT_ID
sha256sum dsar_export/$SUBJECT_ID.tar.gz > dsar_export/$SUBJECT_ID/checksum.txt

The resulting archive contains one CSV per table, a checksum for integrity verification, and a manifest file (plain‑text list of tables and row counts) that can be attached to the fulfillment ticket.

Assembling a JSON payload with jq

jq -n \
  --arg subj "$SUBJECT_ID" \
  --argjson rows "$(cat dsar_export/$SUBJECT_ID/manifest.json)" \
  '{subject_id: $subj, row_count: ($rows | length), data: $rows}' > response.json

This produces a compact JSON document that matches the typical structure expected by downstream compliance tooling (consent management platforms, regulator portals, etc.).

Executing Erasure or Restriction

When the subject invokes the right to be forgotten (Article 17) or restriction (Article 18), the erasure workflow must be idempotent, auditable, and orthogonal to backup retention policies.

SQL‑based deletion with idempotency

psql -h db.internal -U app -d prod -c \
"DELETE FROM orders WHERE user_id = '$(SUBJECT_ID)'; \
 DELETE FROM profiles WHERE user_id = '$(SUBJECT_ID)';"

Because DELETE is naturally idempotent, re‑running the command after a partial failure does not create duplicate side‑effects. Wrap the statements in a database transaction and log the transaction ID to the DSAR audit table.

API‑based deletion

curl -s -X DELETE "$API_ENDPOINT/subjects/$SUBJECT_ID \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -w "\nHTTP %{http_code}\n"

The Idempotency-Key header ensures that repeated requests from a ticketing system do not trigger double‑deletion errors. A 202 Accepted with a Location header pointing to a status‑check endpoint is the typical successful response; 409 Conflict indicates a concurrent erasure in progress.

Verification query after erasure

psql -h db.internal -U app -d prod -c \
"SELECT count(*) AS remaining_rows FROM orders WHERE user_id = '$(SUBJECT_ID)';"

A returned count of 0 (or the exact number of rows that were logically related, if a soft‑delete flag was used) confirms the erasure completed at the database layer.

Validating Completeness and Auditing

Regulators expect a demonstrable chain of evidence that the request was received, processed, and completed within the legal window. Engineering teams typically maintain a DSAR audit log that records every step, the tools used, and the timestamps.

Audit‑trail query

SELECT dsar_id, subject_id, request_type, started_at, completed_at,
       status, tool_used, checksum_provided
FROM dsar_audit
WHERE subject_id = '$(SUBJECT_ID)'
ORDER BY started_at DESC;

This query should return one row per DSAR, with status values such as received, in_progress, exported, erased, or failed.

Checksum‑driven integrity check If an export archive was generated (see the “Compiling the Response” section), the stored checksum.txt must be compared against a recomputed hash after the archive is handed off to the legal team:

sha256sum -c dsar_export/$SUBJECT_ID/checksum.txt

A OK result confirms the file was not corrupted in transit; a mismatch triggers a re‑export and a new audit entry.

Sharp Edges – What Silently Fails

Even well‑specified DSAR workflows break when assumptions about data distribution are wrong. The most frequent production‑grade gotchas include:

Lesson‑learned pattern After a DSAR erasure missed a Redis cache in a prior quarter, the team added a post‑erasure step that flushes the relevant key prefix:

redis-cli -a "$REDIS_PASSWORD" DEL "user_data:$(SUBJECT_ID)*"

The command is idempotent (DEL on a non‑existent key returns 0) and is logged alongside the database DELETE statement. This pattern—flush‑then‑verify—has become the de‑facto standard in the team’s DSAR pipeline.

Automation and Integration Patterns

DSAR handling is rarely a manual, one‑off operation. Most engineering orgs embed the workflow into ticket‑driven pipelines or scheduled jobs.

GitHub Actions trigger on a DSAR ticket label

name: DSAR Processing
on:
  issue_label:
    types: [added]
    labels: [dsar-request]
jobs:
  process:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
      - name: Run locate step
        run: |
          SUBJECT_ID=$(jq -r '.issue.body.subject_id' github.event.issue)
          ./scripts/locate.sh "$SUBJECT_ID"
      - name: Compile export
        run: |
          SUBJECT_ID=$(jq -r '.issue.body.subject_id' github.event.issue)
          ./scripts/export.sh "$SUBJECT_ID"
      - name: Post audit entry
        run: |
          curl -s -X POST "$COMPLIANCE_API/audit" \
            -H "Authorization: Bearer $COMPLIANCE_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"subject_id\":\"$SUBJECT_ID\",\"status\":\"exported\"}"

The pipeline is deliberately linear: locate → compile → audit. If any step exits non‑zero, the workflow stops and flags the ticket for manual review, preventing a partially completed DSAR from slipping through.

Scheduled back‑log reconciliation For organizations that receive DSARs via email or a portal without API integration, a nightly cron job pulls new request identifiers from a shared mailbox, extracts the subject ID, and enqueues them into a message queue (e.g., RabbitMQ or Kafka). Downstream workers dequeue one request at a time, run the locate/compile/erase sequence, and mark the job as complete in a dsar_jobs table. This decouples the intake front‑end from the processing back‑end and provides back‑pressure if the volume exceeds the team’s SLA capacity.

*This reference page is intentionally tool‑agnostic where possible, using standard CLI utilities (curl, psql, jq, tar, redis-cli) and generic SQL patterns. Map the exact endpoints, config keys, and product‑specific flags to your own stack; the data‑flow mechanics, failure modes, and audit‑log patterns described here apply across any system that stores personal data under GDPR jurisdiction.*

This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.