Help · Privacy

What Transform My Role keeps and what it never stores

The user-facing privacy page for Transform My Role. What we read, what we discard, what an audit row contains, why an anonymous session ID is pseudonymous, and how saving and account deletion work.

Headline
The job description, the URL, and any file you upload are read in memory and discarded. We never persist them — anonymous or registered. Only a non-content audit row recording the operational shape of the session is kept.

What we never store

  • The job description text.Pasted text and URL-fetched text are read by the extraction pipeline and discarded once a structured candidate envelope is produced. No copy exists on disk, in logs, or in temp files beyond the upload TTL.
  • The URL itself.Not the host, not the path, not the query string. The audit row carries mode='url' but no URL content. The Cloud-log scrubber strips URL-like strings before logs leave the request.
  • The uploaded bytes.Files route through an in-memory ephemeral processing path with a 60-minute hard-cap TTL. No SMC source-material row is created — the SMC catalog cannot see Transform-My-Role uploads by construction.
  • The filename and any checksum.Filenames can themselves leak title and employer ("BIM_Manager_Acme_Corp.pdf"). The audit row does not record filename, byte count, or checksum.
  • Request headers, IP address, and User-Agent.The audit row carries no request metadata beyond mode, jurisdiction, locale, timings, outcome, and (if you opted in to save) your user ID.

What the audit row records

A single non-content row is written per session to the transform_ingestions table. Its purpose is operational: it lets us answer "how many sessions completed successfully today, in what jurisdictions, in what locales?" without ever recording a job description.

  • modeOne of 'url', 'upload', or 'text-paste'. The shape of the ingest, not its content.
  • jurisdictionAn ISO country / sub-region code (e.g. 'BR', 'IE', 'CA') used to pick the canonical future-ready counterpart. Not the URL host.
  • localeThe locale of the session ('en', 'pt-BR', 'fr'). Operational, not content.
  • started_at / completed_atTimestamps only. No content.
  • outcomeOne of: success, extract_fail, fetch_blocked, size_exceeded, format_unsupported, user_abandoned, rights_declined.
  • opted_in_saveBoolean. True if a registered user clicked Save this plan. Drives an aggregate conversion metric — never linked to plan content.
  • user_id (nullable)Null for anonymous sessions. Set to your user ID if you signed in mid-session or opted in to save. ON DELETE SET NULL — see the deletion section below.

That is the complete list. The migration that creates this table has zero columns that could carry job-description content. An integration test enumerates a forbidden-column list and fails CI if any new column resembles JD content.

About the anonymous session ID

Every Transform-My-Role session mints a random UUID — the session ID. The ID is pseudonymous: it is generated per session, never linked to a fingerprint, never derived from an identifier, and never reused across sessions. A fresh session produces a fresh UUID.

When you sign in mid-session or save the plan, your user_id is recorded on the audit row and on any saved plan; before sign-in, the session is fully anonymous. The session ID is not by itself identifying — it is an internal handle for the in-memory session store.

Retention windows

URL fetch and paste text

Zero persistence. The text crosses through extraction and is discarded immediately. No temp file, no log entry, no DB row contains the text.

File upload

60-minute hard cap. Bytes route through an in-memory ephemeral processing path; if a temp-file fallback ever has to be used, a sweep enforces the 60-minute boundary. After 60 minutes the bytes are guaranteed gone regardless of pipeline state.

If you save the plan

Saving is registered-only. The save action persists the structured features — the extracted candidate envelope, the mapping resolution, the gap synthesis output, the plan package — into the saved_transform_plans table, keyed by your user ID. It does not persist the job description, the URL, the upload bytes, the filename, or any other content-shaped field.

A defensive zod refinement rejects any payload that carries content-shaped keys at write time, regardless of how the call site was constructed. If a future code path ever tried to slip a 'jdText' or 'pasteContent' field through the save endpoint, the write would fail with a privacy-contract error.

Change notifications

If you opt a saved plan in to change notifications, the daily digest is sent to your account email address. The email lists the titles of your own saved plans that changed — it is delivered only to you, the plan's owner. We never log the recipient address or any saved-plan content; a send that fails is recorded with the subscription identifier and the error type only. Every digest has a one-click unsubscribe link, and unsubscribing removes the subscription record entirely.

The rights-acknowledgement tickbox

The paste-text and upload modes are gated by an explicit rights-acknowledgement tickbox. The tickbox affirms that you hold the rights to share the content — typically because you authored it, it was published for general distribution, or your employer permits it. The URL-fetch mode is not tickbox-gated because the fetch happens against the public web with robots.txt respected. The tickbox itself is not persisted as content; only mode='upload' or mode='text-paste' is recorded on the audit row.

Account deletion

Saved plans and change-notice subscriptions cascade on user deletion (ON DELETE CASCADE). If you delete your account, every row referencing your user_id in saved_transform_plans and transform_plan_subscriptions is deleted with the account.

Audit rows behave differently: user_id is nullable with ON DELETE SET NULL. The operational row is preserved (so aggregate per-jurisdiction analytics still work), but its link to your identity is dropped — the row is anonymised, not deleted. If you need the audit row deleted as well, contact support.

The conversational agent panel

The curated agent panel on the plan view answers questions from a pre-baked context built from the structured candidates only. The agent context never carries the job description, the URL, or the upload bytes — this is verified in the 30 curated sample conversations that ship with the surface (10 each in EN / pt-BR / FR). The panel renders a 'This conversation is not saved' indicator; if you sign in and save the plan, the indicator switches to 'Saved to your account; the job description is not retained'.

Cloud logs and operational telemetry

A Pino-compatible log scrubber strips any field name that looks like content (jdText, jdUrl, jdMarkdown, uploadBytes, rawJd, sourceUrl, extractedText, uploadFilename, plus 9 additional bare-noun shapes added during the privacy/legal review) and redacts URL-like strings in free-text fields before logs leave the request. This is opt-in for the Transform-My-Role surface — the scrubber does not mask legitimate URL fields elsewhere in the codebase.

Transform My RoleRead the methodology