Email-capture CTA design under a no-data-storage promise: reuse timestamp-based unregistered-user state instead of a shadow flag, encode only user-changed inputs in a base64url permalink, rate-limit via a payload-free send log, and track clicks with UTM+analytics instead of a shortener.
Designing an email-capture CTA for a public calculator page ('let us email you these results') under a 'we won't store your data unless you ask' promise surfaced four moves that compose well and avoid common over-engineering:
No shadow-user flag needed if the user lifecycle is timestamp-based. The codebase expressed registration as registered_at IS NULL (created via magic link, never onboarded), and create_user() deliberately deferred the welcome email to complete_user_registration(). That means an email-only auto-created account already exists as a first-class state: create the user with email_verified=False, leave registered_at NULL, and 'claiming' is the pre-existing magic-link login flow with zero new code. The only addition worth making is a nullable signup_source provenance column so growth can distinguish capture-created users from abandoned signups — provenance is a column, state is the timestamps.
Permalink = base64url of only the fields the user changed. Calculators track user overrides separately from computed values; encoding just user_overrides ∩ is_input fields (bare names, raw JSON values) keeps URLs short (users touch a handful of fields, defaults solve the rest server- and client-side) and means the permalink stores nothing on your servers. The backend must build byte-identical URLs for the email body, so spec the wire format once (base64url no-padding, compact JSON) and make the frontend decoder re-pad.
Send-log without payload reconciles rate limiting with the privacy promise. An unauthenticated endpoint that sends email to arbitrary addresses and creates users is a spam vector; you need throttling, which needs storage. Log THAT a send happened (user_id, resource, client_ip, timestamp) — never the submitted values. Rate-limit per-user (resolve/create the user first, so repeat submits of the same email throttle correctly) plus per-IP. Also keep the API response constant regardless of whether the account pre-existed, or you've built an email-enumeration oracle.
Click tracking rarely needs a shortener. UTM params on the permalink plus a product-analytics event fired when the page loads with the report param present gives full click-through funnel visibility with zero new PII tables. ESP-native link tracking (e.g. Postmark TrackLinks) is the next rung. An in-house shortener with a click log is almost always the worst option: most work, most stored data, and directly against a no-storage promise.
One conversion detail: when replacing an existing engagement popup's trigger (e.g. 'on 3rd computation'), check the old popup's mobile degradation path first — ours only lit a FAB badge under 1024px, meaning a chat-based email ask would have been invisible to most mobile traffic, which decided modal-vs-chat on evidence rather than taste.