MOLTED EMAIL

GDPR & Privacy Compliance

Export tenant data, delete contacts or message content, and view the decision trace for all data operations.

Molted Email provides built-in tools for GDPR and privacy compliance: data export, contact and tenant deletion, message content erasure, and a full decision trace of all data operations.

All GDPR operations are processed asynchronously. Use the status endpoints to poll for completion.

These endpoints need the session of a person signed in to the dashboard. They are never available to an agent: an API key gets 401, and a dashboard session an agent signed up for itself (through the CLI or MCP) gets 403 {"error": "human_required"}, even when that agent owns the workspace. The deletions are also owner-only: a member of the workspace gets 403.

Data export

Request a full export of your tenant data. The export is packaged as JSON and made available for download once processing is complete.

POST https://api.molted.email/v1/me/export
curl
curl -X POST https://api.molted.email/v1/me/export \
  -H "Cookie: YOUR_SESSION_COOKIE"
Response
{
  "id": "export_abc123",
  "status": "pending",
  "createdAt": "2026-03-31T12:00:00Z"
}

Check export status

GET https://api.molted.email/v1/me/export/:id
curl
curl https://api.molted.email/v1/me/export/export_abc123 \
  -H "Cookie: YOUR_SESSION_COOKIE"
Response
{
  "id": "export_abc123",
  "status": "completed",
  "downloadUrl": "https://...",
  "fileSize": 204800,
  "createdAt": "2026-03-31T12:00:00Z",
  "completedAt": "2026-03-31T12:01:00Z"
}
StatusDescription
pendingExport queued, not yet started.
processingExport in progress.
completedExport ready. Use downloadUrl to download.
failedExport failed. Check errorMessage for details.

Deleting contacts

Delete a single contact

Remove all stored data for a single email address.

POST https://api.molted.email/v1/me/delete/contact
curl
curl -X POST https://api.molted.email/v1/me/delete/contact \
  -H "Cookie: YOUR_SESSION_COOKIE" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com"}'

The address is matched case-insensitively and ignoring surrounding whitespace, so every spelling of it stored for your tenant is found. Other addresses that merely look similar (a +tag variant, dots in a Gmail address) are different addresses and are not touched. It cannot be one of your own mailbox or sender addresses; those requests are rejected with 400. The database part of the deletion runs as a single transaction, so it either completes fully or changes nothing. It:

  • Adds the address to your suppression list permanently (reason legal_request), so it is not emailed again. The suppression list keeps the address in plain form, because that entry is what blocks future sends.
  • Deletes the contact record and its segment memberships, list subscriptions, engagement stats and leases, plus the address's sender allow-list entry and its per-address sender reputation. For each list the person had unsubscribed from, a one-way hash of the address (salted with the list, never the address itself) is kept, so their opt-out from that list still holds if the address is ever added again.
  • Erases the content of every message sent to or received from the address: bodies, subjects, sender display names, Reply-To addresses, raw inbound payloads, authentication results, send payloads, humanizer rewrites and stored humanizer previews.
  • Deletes the attachments of those messages, received and sent: the stored files, their file names and checksums, and the file names in the attachment scan entries of your decision trace. They can no longer be downloaded or attached from the moment the database part commits. A file that is your own content rather than the person's is kept: one you also attached to a send to anyone else (in any status, including sends waiting for approval or scheduled), or one that arrived on someone else's message and was forwarded to this person. The decision trace counts those as attachments_kept_shared.
  • Cancels the address's active journey runs and pending follow-ups, and deletes its journey trigger events, event-stream entries and outbound webhook deliveries that name it.
  • Clears the provider delivery payloads, message timeline payloads (such as clicked links), inbound-hook log fields, bounce details, approval and handoff notes, and outcome metadata that relate to the address.
  • Replaces the address everywhere it is kept (sends, send attempts, inbound messages, threads, thread messages, consent records, delivery history, experiment assignments, follow-ups, outcomes, spam feedback and your decision trace) with a pseudonym: redacted- followed by a keyed HMAC-SHA256 of the address. Delivery metadata (timestamps, status) and your consent history are kept. The key stays on Molted's servers, so nobody can recompute the pseudonym from the address. Rows erased before this change carry an older redacted- value (an unsalted MD5 of the address); the original address is gone, so they are left as they are.

The same transaction replaces the address on the deletion request itself with the pseudonym, so neither the request nor any of the records above keeps the address past that commit (the suppression list aside). It can still exist for a while outside them: in the data of queued or recently finished send jobs (a failed job's data expires from the queue within 7 days; the record of what failed and why is kept separately, without the address), in a small number of older system log entries not tied to your workspace, and in data export files you created before the deletion. The decision trace records how many rows were erased per table and how many attachment files were deleted, identified by the pseudonym, never by the address.

Attachment files are deleted from storage right after that commit. If some can't be deleted (a storage outage, for example), the decision trace records gdpr.deletion.contact_single.attachments_pending with the number of files left. They are no longer downloadable, and a daily cleanup retries them until they are gone, then records gdpr.deletion.contact_single.completed. Each deletion waits only for its own files. If the database part itself fails (for example because it couldn't get a lock on a busy workspace in time), nothing is erased and it is retried automatically, up to five times with increasing delays.

Not erased:

  • Mentions of the address inside the content of other people's messages (for example a colleague quoting it), and an encrypted Reply-To header on someone else's message that points at the address.
  • Your own configuration that names the address, such as mailbox rules and allowed-sender lists. Edit those yourself if needed.
  • Data export files created before the deletion.

Delete all contacts

Remove all contact records for the tenant.

POST https://api.molted.email/v1/me/delete/contacts
curl
curl -X POST https://api.molted.email/v1/me/delete/contacts \
  -H "Cookie: YOUR_SESSION_COOKIE"

Deleting message content

Delete the content and attachments of every message stored for your tenant, while keeping delivery metadata (timestamps, recipient, status).

POST https://api.molted.email/v1/me/delete/message-content
curl
curl -X POST https://api.molted.email/v1/me/delete/message-content \
  -H "Cookie: YOUR_SESSION_COOKIE"

The request takes no body and has no date filter: it applies to all stored messages. It clears subjects, bodies, sender display names, Reply-To addresses and raw inbound payloads, send payloads and humanizer rewrites, deletes stored humanizer previews, and deletes every attachment, received or sent (the stored file, its file name and checksum, and the file name in its scan entry). Addresses are kept. Copies of subjects in provider delivery payloads and event history are not cleared yet. To remove content by age instead, use content retention, where your plan sets one.

As with a contact deletion, attachment files that can't be deleted right away are recorded as gdpr.deletion.message_content.attachments_pending (not downloadable, retried daily) until they are gone, then gdpr.deletion.message_content.completed.

Tenant deletion

Request deletion

Schedule the tenant and all associated data for deletion. A grace period applies before the deletion is executed.

POST https://api.molted.email/v1/me/delete/tenant
curl
curl -X POST https://api.molted.email/v1/me/delete/tenant \
  -H "Cookie: YOUR_SESSION_COOKIE"

What happens to your account

When the deletion runs, your tenant's data is deleted as above and so is your account: your name, email address, password, sessions, and any open verification or sign-in links. If you also belong to another workspace, your account stays for that workspace and only your membership of the deleted one goes.

We keep one thing: a keyed hash (HMAC) of your email address, never the address itself. It can't be turned back into your address, and we only use it to recognise the address if it signs up again, to prevent abuse of the free trial (legitimate interest: fraud and abuse prevention). You can always sign up again with the same address; the new account works normally, without the 3-day free trial on a paid plan.

Cancel deletion

Cancel a pending deletion request during the grace period.

DELETE https://api.molted.email/v1/me/delete/tenant
curl
curl -X DELETE https://api.molted.email/v1/me/delete/tenant \
  -H "Cookie: YOUR_SESSION_COOKIE"

Decision trace

All GDPR operations are recorded in the decision trace. Use this to demonstrate compliance with data subject access requests and regulatory audits.

GET https://api.molted.email/v1/me/audit-log
curl
curl "https://api.molted.email/v1/me/audit-log?limit=50" \
  -H "Cookie: YOUR_SESSION_COOKIE"
Response
{
  "entries": [
    {
      "id": "log_xyz789",
      "event": "gdpr.export.requested",
      "userId": "user_abc",
      "createdAt": "2026-03-31T12:00:00Z",
      "details": {}
    }
  ]
}

Event types

EventDescription
gdpr.export.requestedData export was requested.
gdpr.export.completedExport processing completed.
gdpr.deletion.requestedA contact, contacts or message-content deletion was requested.
gdpr.deletion.<scope>.erasedThe database part of a deletion committed (<scope> is contact_single, message_content or contacts). Records the rows erased per table and, for a contact, the address's pseudonym (dataSubject).
gdpr.deletion.<scope>.attachments_pendingSome attachment files could not be deleted from storage yet. Records how many were deleted, failed and are left. They are not downloadable, and a daily cleanup retries them.
gdpr.deletion.<scope>.attachments_stuckWritten once when a deletion has waited more than 7 days for its attachment files. The files are still not downloadable; contact support if you see this.
gdpr.deletion.<scope>.completedEvery part of the deletion is done, attachment files included.
gdpr.tenant_deletion.requestedTenant deletion was scheduled.
gdpr.tenant_deletion.cancelledA scheduled tenant deletion was cancelled.