GDPR Data Retention Policy
This guide explains how the Push Notification System (Hober) stores, retains, and deletes personal data in compliance with GDPR. It covers retention periods per data category, deletion cascade behaviour, audit logging, bulk deletion constraints, the API endpoints for triggering deletion, and an operator runbook for handling manual data removal requests.
Retention periods
| Data category | Table(s) | Retention period | Notes |
|---|---|---|---|
| Subscriber PII | subscribers | Until deletion requested | external_id, email, attributes |
| Device tokens | devices | Until deletion requested | Cascade-deleted with subscriber |
| List memberships | subscriber_list_memberships | Until deletion requested | Cascade-deleted with subscriber |
| Delivery logs | notification_delivery_logs | 90 days | Anonymised on subscriber delete; row retained for analytics |
| Audit logs | subscriber_deletion_audit | 7 years | Required for compliance evidence; never deleted |
Hober does not proactively expire subscriber PII. Deletion is always initiated by an operator or by the subscriber via your product's account-deletion flow (which should call the Hober deletion API described below).
Deletion cascade behaviour
When a subscriber is deleted, Hober applies the following cascade rules atomically within a single database transaction:
Hard deletes
The following rows are permanently removed:
subscribers— the subscriber record itselfdevices— all devices belonging to the subscriber (ON DELETE CASCADE)subscriber_list_memberships— all list memberships for the subscriber (ON DELETE CASCADE)
Once hard-deleted, these records cannot be recovered. Ensure any downstream systems (CRM, analytics warehouse) are notified before triggering deletion.
Anonymisation (delivery logs)
notification_delivery_logs rows are not deleted. Instead, the foreign keys are nullified to preserve aggregate delivery metrics while removing the link to the individual:
subscriber_id = NULL -- anonymised
device_id = NULL -- anonymised
The log row (timestamp, status, provider response) is retained for 90 days from delivery date to support throughput analytics and SLA reporting. After 90 days the row is removed by a scheduled purge job.
Audit log — subscriber_deletion_audit
Every deletion operation (single or bulk) writes one row to subscriber_deletion_audit. This table is append-only and is never purged.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
tenant_id | uuid | The tenant that owns the deleted subscriber |
subscriber_id | uuid | UUID of the deleted subscriber |
deleted_by | uuid | User ID of the operator or service account that triggered the deletion |
subscriber_count | integer | 1 for single-subscriber deletions; N for bulk deletions |
deleted_at | timestamptz | Wall-clock time of the deletion (UTC) |
Use this table to produce GDPR erasure evidence. For bulk deletions, one audit row is written per subscriber in the batch, all sharing the same deleted_at timestamp.
Bulk deletion limits and rate limiting
The bulk deletion endpoint accepts a maximum of 1,000 subscriber IDs per request. Requests exceeding this limit are rejected with HTTP 422 Unprocessable Entity.
IDs included in the request but not found in the tenant's subscriber set are returned in the not_found array of the response body and are not treated as errors — the overall request succeeds for the IDs that were found and deleted.
Rate limiting applies at the tenant level:
| Plan | Bulk deletion rate limit |
|---|---|
| Starter | 10 requests / minute |
| Growth | 50 requests / minute |
| Premium | 200 requests / minute |
For large-scale erasure operations (for example, a data-subject access request requiring deletion of tens of thousands of subscribers), batch requests in chunks of 1,000 and respect the rate limit for your plan. See the operator runbook below for a scripted approach.
Triggering deletion via the API
See the Subscribers API reference for full request/response schemas.
Single subscriber deletion
DELETE /api/v1/subscribers/{id}
Authorization: Bearer <api_key>
{id} is the Hober subscriber UUID (the id field returned by POST /api/v1/subscribers/upsert).
Success response — 204 No Content.
Error responses:
| Status | Meaning |
|---|---|
401 | Missing or invalid bearer token |
404 | Subscriber not found |
Operator runbook
Use this runbook when a data subject submits a GDPR erasure request and you need to delete one or more subscribers manually.
Step 1 — Identify the subscriber UUID
If you have the subscriber's external_id (your internal user ID), look up the Hober UUID:
curl -s -X GET \
"https://api.hober.io/api/v1/subscribers?external_id=<your_user_id>" \
-H "Authorization: Bearer $Hober_API_KEY" \
| jq '.id'
Record the returned UUID as $SUBSCRIBER_ID.
Step 2 — Trigger deletion
curl -s -X DELETE \
"https://api.hober.io/api/v1/subscribers/$SUBSCRIBER_ID" \
-H "Authorization: Bearer $Hober_API_KEY"
A 204 No Content response confirms deletion. If you receive 404, the subscriber has already been deleted or the UUID is incorrect.
Step 3 — Verify the deletion
Repeat the lookup from Step 1: a 404 (or an empty result for the external_id query) confirms the subscriber is gone. Every deletion is also recorded in Hober's internal deletion audit trail — including when and by whom — which is retained for compliance evidence.
Step 4 — Bulk erasure (scripted)
For erasure requests covering many subscribers, loop over the single-delete endpoint. Replace ids.txt with a file containing one Hober subscriber UUID per line.
#!/usr/bin/env bash
set -euo pipefail
TOKEN="${HOBER_ACCESS_TOKEN:?HOBER_ACCESS_TOKEN is required}"
BASE_URL="https://api.hober.io"
IDS_FILE="${1:?Usage: $0 ids.txt}"
DELETED=0
while IFS= read -r ID; do
[[ -z "$ID" ]] && continue
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE "$BASE_URL/api/v1/subscribers/$ID" -H "Authorization: Bearer $TOKEN")
case "$HTTP_CODE" in
204) DELETED=$((DELETED + 1)) ;;
404) echo "already gone: $ID" ;;
*) echo "ERROR: $ID returned HTTP $HTTP_CODE" >&2; exit 1 ;;
esac
# Respect rate limits — adjust for your plan
sleep 0.3
done < "$IDS_FILE"
echo "Done. Total deleted: $DELETED"
Step 5 — Document the erasure
Record the following in your erasure request ticket:
- Date and time of deletion (UTC)
- Subscriber UUID(s) deleted
deleted_byuser ID used- Confirmation of the verification lookup (Step 3)
Retain this evidence for the 7-year audit log period required by GDPR Article 5(2).
Subscriber deletion is permanent. Hard-deleted rows cannot be recovered from the database. Ensure you have confirmed the correct subscriber UUID before proceeding.
Delivery log rows are anonymised (subscriber and device foreign keys set to NULL), not deleted, when a subscriber is removed. The anonymised rows are purged automatically after 90 days. This means aggregate delivery metrics are unaffected by erasure operations.