Docs

Erasure (right to erasure)

When one of your users asks to be forgotten, one call removes their personal data from Softechlog — and leaves a sealed record that you did it, without the data itself.

When to use it

Use erasure for a data-subject request under GDPR Art. 17 (or a similar law), or when you close an account whose history you have no reason to keep. It works on every plan. For routine clean-up of old events you don't need it — retention already deletes them.

Stop sending events for the user first. Erasure removes what is stored; an event tracked afterwards creates a fresh, unrelated record for the same id. A request that races the very end of an erasure can fail once with 500; the SDK's retry then stores it as that fresh record.

Requesting an erasure

From the dashboard: open the user and click Erase user…, or use Compliance → Erase a user…. From your code:

Terminalcurl
curl https://api.softechlog.com/v1/erasures \
  -H "Authorization: Bearer stl_sk_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "actor_id": "user_123", "mode": "delete", "target_types": ["user"], "reference": "DSR-2026-0142" }'

# 201 → finished;  202 → finishing in the background (poll GET /v1/erasures/{id})
Node.js@futuregensystems/softechlog
import { Softechlog } from '@futuregensystems/softechlog';

const log = new Softechlog({ secretKey: process.env.SOFTECHLOG_SECRET_KEY! });

let erasure = await log.erase({ actorId: 'user_123', reference: 'DSR-2026-0142' });
if (erasure.status !== 'completed') {
  erasure = await log.waitForErasure(erasure.id);   // throws if it fails or takes > 60 s
}

// Keep this record with the ticket — it never contains the user id.
await saveDsrEvidence(erasure);
Pythonsoftechlog
from softechlog import Softechlog

log = Softechlog(secret_key=os.environ["SOFTECHLOG_SECRET_KEY"])

erasure = log.erase("user_123", mode="redact", reference="DSR-2026-0142")
if erasure.status != "completed":
    erasure = log.wait_for_erasure(erasure.id)   # raises if it fails or times out

# Async: await log.aerase(...), await log.await_for_erasure(...)
FieldDefaultMeaning
actor_idrequiredThe user's id in your system — the actor.id you track with.
modedeletedelete or redact — see below.
target_types["user"]Target types whose target_id is this user's id, so mentions in other people's events are scrubbed. [] leaves mentions alone. Up to 10.
referencenoneYour ticket id (≤ 200 chars). It is stored and appears in the evidence event, so put no personal data in it.

Delete or redact

Delete (the default) removes everything the user did; your customer's account audit trail loses those entries. Redact keeps what happened and when, and removes who did it, their IP, device, metadata and targets. Use redact only if you have a legal basis to keep the record (GDPR Art. 17(3), e.g. a legal obligation or legal claims).

Eventsdeleteredact
The user's own eventsDeletedKept: id, action, tenant_id, target_type, capture_mode, occurred_at, created_at.
Removed: actor, session_id, ip_address, user_agent, metadata, target_id, target_name. erasure_id is set.
Anonymous events in their sessions (page views before identify())DeletedRedacted the same way
Other people's events that name the user as a target (target_type in target_types and target_id = the user)Never deleted, in either mode. target_id, target_name and metadata are removed and erasure_id is set; the other person's actor, IP, device and session are untouched.
Their sessions and their actor record (name, email, avatar)Deleted. GET /v1/actors/user_123 answers 404 and feed tokens for them read nothing.

We say redact, not anonymise: whether redacted events are anonymous depends on your data (e.g. an account with a single user, where tenant_id alone identifies them). Redacted events show up in feeds and the dashboard as Erased user.

An unknown actor_id is not an error: the erasure still scrubs target mentions and completes with actor_found: false.

201, 202 and waiting

Small erasures finish inside the request and answer 201 with status: "completed". Large ones answer 202 and finish in the background (pending → running → completed); poll GET /v1/erasures/{id}, or call waitForErasure() / wait_for_erasure(). Asking again for the same user while one is in flight returns the existing erasure with 202. A failed erasure (after 5 attempts) carries an error class name — request it again. All endpoints are in the API reference.

The evidence event and your records

When an erasure completes, Softechlog writes one event to your own log — sealed into the tamper-evident chain like any other, and not counted toward your plan:

Evidence eventJSON
{
  "action": "softechlog.actor.erased",
  "actor": null,
  "tenant_id": null,
  "target_type": "erasure",
  "target_id": "7c1e…",                 // the erasure id
  "capture_mode": "manual",
  "metadata": {
    "erasure_id": "7c1e…", "mode": "delete", "reference": "DSR-2026-0142", "actor_found": true,
    "events_deleted": 212, "events_redacted": 0, "target_refs_redacted": 3, "sessions_deleted": 9,
    "affected_count": 215, "affected_hash": "64 hex"
  }
}

affected_hash is a SHA-256 over every affected event id and what happened to it, so verification can prove each missing or redacted event was covered by this erasure rather than tampered with. Store the returned Erasure object (the dashboard's Download record button saves it as JSON) with the request ticket: it is your accountability record under Art. 5(2) and your Art. 30 records, and it contains no personal data.

What stays behind

  • Erasure records keep a keyed hash of the user id (so you can look an erasure up by id without us storing it), the counts, your reference and opaque event ids. The plaintext id is dropped as soon as the erasure completes or fails.
  • Checkpoints keep an erased event's opaque id and timestamp until retention prunes them, so verification can show the event was erased rather than tampered with.

What erasure doesn't touch

  • Other people's free-form metadata. If another user's event mentions this person inside metadata without naming them as the target, it is not found — keep personal data out of metadata, or use targets.
  • Usage counters (monthly event counts) — they hold no personal data.
  • Short-lived rate-limit keys, which expire on their own within minutes.
  • Database backups. Erased data can remain in database backups until they rotate out. If we ever restore one, we re-apply every erasure recorded in it before the service resumes — but an erasure completed after that backup was taken isn't in it. In that case we'll tell you the backup's timestamp so you can re-submit any erasures made since then (keep your own record of erasure requests, e.g. the reference you send).