# Privacy and consent

What the TryFast script stores in your visitors' browsers, what it sends, how long we keep it, how to connect your cookie banner, and how to erase one visitor.

## In short

- **Consent first.** New projects wait for your cookie banner: before consent, the script stores nothing, sends nothing and shows your original pages.
- **One first-party cookie.** After consent, a random visitor id in the cookie `tf_vid` on your own domain, expiring at most 390 days after it was created. Visits never extend it.
- **No IP addresses stored.** We count visitors with the random id, never with their IP address.
- **No cross-site tracking.** The id belongs to your website only; the same browser on two websites that use TryFast cannot be linked.
- **Attributes stay in the browser.** Values you set for targeting with `tryfast.set()` or `tryfast.identify()` are never sent to us.
- **We are your processor.** TryFast processes visitor data on your behalf under the [Data Processing Agreement](../legal/data-processing-agreement.md), hosted in Germany.

## Consent modes

Each project has a consent mode (`consent_mode`) and, for the default mode, a choice of what happens before the visitor decides (`before_consent`).

| Mode | Before the visitor decides | After consent |
|---|---|---|
| `required` with `before_consent: control` (default) | Nothing is stored, nothing is sent, no variation is applied: everyone sees your original pages, and the page is shown at once. | The script starts on the same page view. On later page views the stored consent (`tf_c`) applies until you call `consent(false)`. |
| `required` with `before_consent: memory` | Variations are applied with a temporary id kept in memory only; events wait in memory; nothing is written to the browser and nothing is sent, also not to your dataLayer. | The id is stored and the waiting events are sent. On refusal, or when the visitor leaves the page first, they are dropped. |
| `exempt` | Runs without waiting for a banner choice, with restrictions (below). | Not applicable. |
| `not_required` | Runs immediately and stores the visitor id. | Not applicable. |

With `memory`, a visitor who never consents may see different variations on different pages, and only visitors who consent are counted. Split URL tests do not run before consent in this mode, because the redirect would need to store which variation the visitor saw; those visitors see the original page. If the browser already has a TryFast visitor cookie (for example after consenting on another subdomain of your site), it is read, never changed, so the visitor keeps one id. Ask your legal team whether applying variations before consent fits your setup.

### Where each mode needs a banner

- **`required`** is the right choice in Germany and most EU countries. A/B testing is not covered by the exemptions for strictly necessary cookies; in Germany it is assessed case by case under § 25 TDDDG, and there is no general exemption. Your banner must call `tryfast.push(["consent", true])` when the visitor accepts; without that call, TryFast never runs.
- **`exempt`** follows the French CNIL's exemption for audience measurement, which accepts A/B testing without consent when every condition holds: you inform visitors in your privacy notice, they can object (offer the opt-out link below), the data is used only to measure your audience and test your pages, it is not combined with other data (no CRM or advertising data in attributes), it stays limited to your website, IP addresses are not used in full, and the id lives at most 13 months. TryFast covers its share: no IP addresses are stored, `tf_vid` expires 390 days after creation, `identify()` is ignored, data never leaves your project, and dataLayer forwarding is switched off. This mode is not a general "no banner" setting: other countries, Germany among them, have no such exemption. Use it only after your legal team has confirmed it for every country your website serves.
- **`not_required`** runs without any consent check. You are responsible for the legal basis, for example when your website collects consent for all processing before any page with TryFast loads, or when the law that applies to your visitors does not require consent.

### Change the consent mode

In the app, open the project's settings. Through the API, an owner or admin sends:

```
curl -X PATCH https://tryfast.eu/api/v1/projects/prj_k2L9mQ0aZx \
  -H "Authorization: Bearer $TRYFAST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings": {"consent_mode": "required", "before_consent": "control"}}'
```

The consent mode changes what your visitors' browsers do, so it needs the publish permission and the role owner or admin; API keys and agents without it get an approval link instead. The change reaches every visitor within 2 minutes.

## Connect your cookie banner

Your banner tells TryFast about the visitor's choice with one call. It works before and after the script has loaded, because `window.tryfast` is a queue until then:

```html
<script>
  window.tryfast = window.tryfast || [];
  window.tryfast.push(["consent", true]);  // the visitor accepted
  // window.tryfast.push(["consent", false]);  // the visitor declined or withdrew
</script>
```

- `true` starts TryFast on the current page view and stores the choice (`tf_c`), so later pages apply variations before your banner script has loaded.
- `false` deletes TryFast's storage in the browser, drops waiting events and stops tracking.
- Assign TryFast to the consent category your banner uses for statistics, analytics or performance, never to strictly necessary cookies.
- Send `false` only when the visitor actively declines or withdraws. Before they decide, send nothing: with `before_consent: memory`, an early `false` would drop the waiting events.

In the modes `exempt` and `not_required` the call is not needed.

### Test without a banner

Open your website, then run this in the browser console:

```js
tryfast.push(["consent", true])
```

`tryfast.state().consent` now reads `"granted"`, and the install status in the app switches to receiving data within a minute. Run `tryfast.push(["consent", false])` to reset.

### Cookiebot

Assign TryFast to the Statistics category:

```html
<script>
  window.tryfast = window.tryfast || [];
  function tryfastCookiebot() {
    window.tryfast.push(["consent", !!(window.Cookiebot && window.Cookiebot.consent.statistics)]);
  }
  window.addEventListener("CookiebotOnAccept", tryfastCookiebot);
  window.addEventListener("CookiebotOnDecline", tryfastCookiebot);
</script>
```

Cookiebot fires these events when the visitor decides and on every page view after that, so returning visitors are handled too.

### Usercentrics

Add TryFast as a data processing service named `TryFast`, and set a window event name (here `ucEvent`) under Implementation, Data Layer and Events in the Usercentrics admin:

```html
<script>
  window.tryfast = window.tryfast || [];
  window.addEventListener("ucEvent", function (e) {
    var d = e.detail;
    if (!d || d.event !== "consent_status") return;
    if (d["TryFast"] === true) window.tryfast.push(["consent", true]);
    else if (d.type === "explicit") window.tryfast.push(["consent", false]);
  });
</script>
```

Use the exact service name you configured as the key.

### OneTrust

Assign TryFast to the Performance Cookies category (`C0002` in the default setup):

```html
<script>
  window.tryfast = window.tryfast || [];
  function OptanonWrapper() {
    var granted = (window.OnetrustActiveGroups || "").indexOf(",C0002,") !== -1;
    if (granted) window.tryfast.push(["consent", true]);
    else if (window.OneTrust && window.OneTrust.IsAlertBoxClosed()) window.tryfast.push(["consent", false]);
  }
</script>
```

OneTrust calls `OptanonWrapper` on load and on every change. If your site already defines `OptanonWrapper`, add the three lines to it instead.

### Didomi

Add TryFast as a custom vendor in the Didomi console and use its vendor id (here `c:tryfast`):

```html
<script>
  window.tryfast = window.tryfast || [];
  window.didomiOnReady = window.didomiOnReady || [];
  window.didomiEventListeners = window.didomiEventListeners || [];
  function tryfastDidomi() {
    var D = window.Didomi;
    if (!D) return;
    var vendor = D.getCurrentUserStatus().vendors["c:tryfast"];
    if (vendor && vendor.enabled) window.tryfast.push(["consent", true]);
    else if (!D.shouldConsentBeCollected()) window.tryfast.push(["consent", false]);
  }
  window.didomiOnReady.push(tryfastDidomi);
  window.didomiEventListeners.push({ event: "consent.changed", listener: tryfastDidomi });
</script>
```

### IAB TCF v2.2 banners

TryFast is not a registered IAB vendor, so check the purposes your banner shows for it. This example requires purpose 1 (store and access information on a device) and purpose 10 (develop and improve services); use the purposes your legal team chose. If your banner lets you add TryFast as a non-IAB vendor, check that consent instead.

```html
<script>
  window.tryfast = window.tryfast || [];
  if (typeof window.__tcfapi === "function") {
    window.__tcfapi("addEventListener", 2, function (tcData, success) {
      if (!success) return;
      if (tcData.eventStatus !== "tcloaded" && tcData.eventStatus !== "useractioncomplete") return;
      var p = tcData.purpose.consents;
      window.tryfast.push(["consent", !!(p[1] && p[10])]);
    });
  }
</script>
```

Place it after your banner's own script so that `__tcfapi` exists.

### Google Tag Manager and other banners

In Google Tag Manager, create a Custom HTML tag with the snippet from "Connect your cookie banner" (`true`) and fire it with the trigger your banner provides when the visitor accepts the category you assigned TryFast to; add a second tag with `false` for an explicit decline. Any other banner works the same way: call `true` when the visitor accepts and `false` when they decline or withdraw.

## What the script stores in the browser

Nothing is written before consent, unless the consent mode is `exempt` or `not_required`.

| Key | Where | What | Lifetime |
|---|---|---|---|
| `tf_vid` | first-party cookie on your registrable domain (or the cookie domain you set), mirrored in local storage | random visitor id (128 bits) and the day it was created | 390 days after creation; never extended by visits |
| `tf_a` | local storage | which variation this visitor was assigned in each experiment | entries are removed 30 days after an experiment leaves your script |
| `tf_a_seen` | local storage | when each experiment was last seen in your script | same as `tf_a` |
| `tf_s` | local storage | current visit: visit number, start and last activity, landing page, referrer origin, traffic type, page views, `utm_*` values; a new visit starts after 30 minutes without activity | 390 days |
| `tf_u` | local storage | attributes you set with `tryfast.set()` or `tryfast.identify()`; never sent to us | 390 days |
| `tf_q` | local storage | events that could not be sent yet (at most 50, none older than 24 hours) | until sent |
| `tf_c` | local storage | `"1"` once the visitor consented | until `consent(false)` |
| `tf_oo` | cookie | `"1"` after the visitor opted out | 390 days |
| `tf_pv`, `tf_el`, `tf_ed`, `tf_dbg`, `tf_off` | session storage | preview link, visual editor link and session, debug flag, "disable for this tab" flag; written only when you or your team open such a link | until the tab closes |
| `tf_rd` | session storage | hands a split URL test assignment to the redirect target page | until that page has loaded |

The preview, editor, debug and opt-out parameters (`tf_preview`, `tf_editor`, `tf_debug`, `tf_disable`, `tf_optout`) work before consent: they write only session storage, and the opt-out writes only its own restricting cookie.

## What the script sends

After consent, the script sends batches of events to `https://cdn.tryfast.eu/e`, a host that never reads or sets cookies:

- **With every batch:** the visitor id, a visit id, device class (desktop, tablet, mobile), browser family, new or returning, traffic type (direct, organic, paid, email, social, referral), the page's origin, and the script and configuration versions.
- **Page views:** the page URL reduced to origin, path and `utm_*` parameters (every other query parameter and the fragment are removed), and the referrer's origin.
- **Exposures:** which experiment and variation the visitor saw.
- **Conversions:** the goal key and, if you send them, revenue, currency, order id and up to 10 properties.

IP addresses and the user agent reach our server with each request; we use them only in memory for rate limits, bot filtering and the device class, and never store them. Keep personal data such as names, email addresses or customer numbers out of URL paths, goal keys, order ids and properties.

When you enable the dataLayer setting and your page has a `window.dataLayer`, the script also pushes `tryfast_exposure` and `tryfast_conversion` events there, never before consent and never in `exempt` mode. What your tags do with them is your own processing.

## How long we keep it

| Data | Kept |
|---|---|
| Raw event logs (for recovery and reprocessing) | 30 days |
| Per-visitor experiment data (assignment, conversions) | while the experiment runs, then 90 days after it stops; afterwards only totals and the final results remain |
| Daily visit data per visitor | 35 days |
| Hashed order ids (each order counts once) | 90 days |
| Monthly tested-visitor list (plan usage) | the current month, and the previous month until the 3rd |
| Recent events shown in the app and to agents | 24 hours, in memory only |
| Totals without visitor data | 400 days |
| Experiment results | until you delete the experiment, project or workspace |
| Backups | deleted data leaves them within 35 days |

Deleting a project makes its script do nothing at once and removes its visitor data within minutes. The event database stores visitor ids only as hashes computed per project.

## Erase one visitor

1. **Find the visitor id.** On the visitor's device, run `tryfast.state().visitorId` in the browser console on your website, or read the `tf_vid` cookie: the id is the part before the dot.
2. **Erase it.** An owner or admin (signed in), or an API key with write permission, sends:

```
curl -X POST https://tryfast.eu/api/v1/projects/prj_k2L9mQ0aZx/visitors/erase \
  -H "Authorization: Bearer $TRYFAST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"visitor_id": "f3K9xQ2mPa7Lz0Bc5Rt8Wq"}'
```

The response is `202`. Within a minute, that visitor's assignments, conversions, daily visit rows and monthly tested-visitor row in the project are deleted, and the erasure appears in the activity log. Totals already counted and final results are not recalculated; raw event logs expire within 30 days and backups within 35 days. For safety, AI agents cannot erase visitors through the MCP server.

3. **Stop further collection.** Ask the visitor to withdraw consent in your banner or to opt out (below); otherwise the same browser is counted again on the next visit.

## Let visitors opt out

Any visitor can switch TryFast off on your website by opening it once with `?tf_optout=1`, for example `https://acme-shop.de/?tf_optout=1`. The script then stores the cookie `tf_oo` (390 days), deletes its other storage, and applies no variations and sends nothing on later page views. `?tf_optout=0` undoes it. You can also call `tryfast.optOut()` from a link or button in your privacy notice. The opt-out works in every consent mode and before consent.

The same link excludes your own browser from experiments while you work on your website.

## Text for your cookie banner and privacy notice

For your banner's cookie list:

| Name | Provider | Purpose | Expiry |
|---|---|---|---|
| `tf_vid` | your domain (first party), set by TryFast | A/B testing: recognizes the browser so it sees the same version of a page and its conversions are counted | 390 days after creation |
| `tf_oo` | your domain (first party), set by TryFast | remembers an opt-out from A/B testing | 390 days |

Add the local storage keys `tf_a`, `tf_a_seen`, `tf_s`, `tf_u`, `tf_q` and `tf_c` if your banner lists local storage.

A paragraph you can adapt for your privacy notice (check it with your legal team):

> We use TryFast, an A/B testing service of subprocess.eu/company, Osloer Str. 83, 13359 Berlin, Germany, to test changes to our website. With your consent, TryFast stores a random identifier in a cookie on our domain (`tf_vid`, at most 390 days) and in your browser's local storage, shows you one version of a page, and records which version you saw and whether you then took actions such as a purchase. IP addresses are not stored, and the data is not linked to other websites. TryFast processes the data on our behalf in Germany under a data processing agreement; per-visitor data is deleted at the latest 90 days after a test ends. Legal basis: your consent (Art. 6(1)(a) GDPR and § 25(1) TDDDG), which you can withdraw at any time in our cookie settings. You can also opt out of all tests by opening our website with `?tf_optout=1`.

## Frequently asked questions

### Is TryFast cookieless?

No. TryFast uses one first-party cookie and local storage after consent. Under EU privacy law, local storage counts the same as cookies, so a "cookieless" setup would not avoid the consent question anyway.

### Does A/B testing need a cookie banner?

In Germany and most EU countries, yes: A/B testing is not covered by the exemptions for strictly necessary cookies. TryFast waits for your banner by default. France's CNIL accepts A/B testing without consent under strict conditions; see the `exempt` mode above.

### Which consent category should TryFast be in?

Statistics, analytics or performance, depending on your banner's names. Not strictly necessary, and not marketing: TryFast data is not used for advertising.

### What do visitors see before they decide?

With the default settings, your original pages, shown without delay. Variations start on the page view where the visitor consents.

### Are results biased when only consenting visitors are counted?

The split between control and variations happens after consent, so both sides get the same mix of visitors and the comparison stays fair. The result describes visitors who consent; visitors who decline always see your original pages.

### Does TryFast set third-party cookies?

No. The only cookies are `tf_vid` and, after an opt-out, `tf_oo`, both on your own domain. The script host `cdn.tryfast.eu` never reads or sets cookies.

### Does Safari keep the visitor id for 390 days?

No. Safari and some other browsers delete storage written by scripts after 7 days without a visit, so such visitors return as new visitors and may see another variation. Both sides of an experiment are affected equally. [Limitations](limitations.md) explains the effect on results.

### Do we need a data processing agreement?

Yes, and you already have one: the [Data Processing Agreement](../legal/data-processing-agreement.md) is part of our terms on every plan. More answers for your DPO are on the [compliance page](../legal/compliance.md#frequently-asked-questions).
