Intempt Docs
GuidesPrivacy

Protecting user data

Opting users out of tracking, anonymizing identifiers, and keeping sensitive input out of capture, with the exact method for every Intempt SDK.

Overview

Intempt processes data on your instructions. Under GDPR you are the data controller and Intempt is the data processor; under CCPA you are the business and Intempt is the service provider. What reaches Intempt is your decision, and this page covers the controls that let you honor a user who asks not to be tracked.

Tracking is enabled by default. Call optOut() before any other SDK method to hold collection until a user consents, and optIn() once they do.

How it works

Two controls, different jobs

These are easy to confuse and they stop different things.

What it stopsWhere you set it
ConsentOne category of processing, like marketing emailconsent(), or a user's Privacy tab
Tracking opt-outAll event collection for that useroptOut() in the SDK

Revoking a consent category doesn't stop event collection. optOut() does.

What opt-out does

While a user is opted out, the SDK keeps working but stops collecting. Calls to track(), identify() and the rest return without doing anything, so you don't need to guard every call site.

On the mobile SDKs, optOut() also discards events already queued on the device, so anything captured just before the user objected never uploads. Queued consent records are deliberately kept, since they're the evidence of the user's decision.

📘 Good to know

Opt-out and erasure are different, and a user asking to be forgotten usually needs both. Erasure removes what Intempt holds now. Opt-out stops new data arriving. Running one without the other leaves the job half done. See Managing DSR.

Opt-out by SDK

SDKOpt outOpt back inCheck status
JavaScriptoptOut()optIn()isOptedIn(), hasOptedOut()
iOSoptOut()optIn()isOptedIn(), hasOptedOut()
AndroidoptOut()optIn()isOptedIn(), hasOptedOut()
React NativeoptOut()optIn()isOptedIn(), hasOptedOut()
NodeoptOut()optIn()isOptedIn(), hasOptedOut()
Pythonopt_out()opt_in()is_opted_in(), has_opted_out()
PHPoptOut()optIn()isOptedIn(), hasOptedOut()

Python uses snake_case because that's what Python code looks like. Everything else is identical across SDKs.

📘 Good to know

JavaScript and iOS also accept isUserOptIn(), the name they originally shipped. It still works and returns the same value, but isOptedIn() is the name to use in new code.

Starting with collection paused

If you gate on consent, you don't want the window between your SDK initializing and your banner returning a decision. Set optOutByDefault and nothing is collected until you call optIn().

intempt.init('YOUR_PUBLIC_KEY', { optOutByDefault: true });

// later, once your consent banner returns a decision
if (userAccepted) {
  intempt.optIn();
}

Without it, tracking is enabled from initialization. With it, the SDK stays idle until you say otherwise.

Client-side opt-out doesn't cover your server

An opt-out set in a browser or an app is stored on that device. It has no effect on events your backend sends. If you track server-side, your backend owns the opt-out state: store it with the user record, check it before sending, and don't rely on the client SDK having been called.

Getting started

Call optOut() before anything else, then optIn() once the user agrees.

intempt.optOut();

// later, once your consent banner returns a decision
if (userAccepted) {
  intempt.optIn();
}

Pseudonymize the identifier

Intempt doesn't need to know who someone is, only that a set of events belongs to one person. Identify users with a stable, non-identifying ID your own system owns.

Never use an email address as the identifier. Two reasons, and the second is the one that hurts. Emails change, so the same person becomes two profiles. And the identifier on an event is fixed once it's recorded, so historical events keep whatever you sent. Picking a value that can change is not something you can correct later.

If your internal ID is itself identifying, hash it before sending and use the hash everywhere.

📘 Good to know

Hashing is pseudonymization, not anonymization. A pseudonymized value is still personal data under GDPR and still needs a lawful basis. A plain SHA-256 of an email is also reversible in practice, because emails are predictable enough to guess and check against. Use HMAC-SHA256 with a secret key your backend holds, not a bare hash.

See Why a single profileId matters.

Where hashing belongs

A client SDK runs on someone else's device, so it can't keep a secret. Anything in a browser bundle or an app binary is readable, including a key. That decides where each control lives.

Client SDKs (JavaScript, iOS, Android, React Native)Server SDKs (Node, Python, PHP)
KeyPublic write key. Never a private or admin keySecret key, held on your server
HashingNot here. Hash before the value reaches the clientHere
Opt-outoptOut() on the deviceYour backend owns the state

If you need hashed identifiers, compute them on your backend and send the result. A hash computed in the browser protects nothing, because the raw value was already in the browser.

📘 Good to know

Hashing for destination matching and hashing for protection are not the same operation and can't be swapped. Ad destinations recompute a plain SHA-256 of a normalized value and match on it, so a keyed hash matches nothing and the integration returns zero results without erroring. Protection needs the key. Matching needs the absence of one.

Keep sensitive input out of capture

Autocapture is enabled by default, and it reads text from the interfaces it tracks. Exclude anything that could hold personal data, or disable text capture at initialization.

SDKControl
AndroiddoNotCaptureText(view) for one view, or isTextCaptureEnabled: false in config
iOSAutocaptureOptions.controlChanges = false at initialization

Password fields, search boxes and free-text forms are the usual sources. Users type their own names into search boxes.

Use cases

  1. A cookie banner returns a rejection, so you call optOut() and no events are collected until the user changes their mind.
  2. You want consent-first behavior, so optOut() runs at startup before any other SDK call and optIn() runs only after acceptance.
  3. A user emails support asking to be forgotten. Submit an erasure request, then opt them out so new events stop arriving.
  4. Your app has a password field being picked up by autocapture. Exclude it with the per-view control.
  5. Your backend sends events for users who opted out in the browser. Store the opt-out with the user record and check it server-side before sending.
  6. Your internal user IDs are sequential and guessable, so you hash them before sending and use the hash consistently.
  7. Support needs to know whether a user is currently opted out before answering a complaint. Check the status method for your SDK.

Where to go next

On this page