Privacy
Tapback's answer to reader privacy is to hold as little as possible. There are no reader accounts, no cookies, no fingerprinting, and no third-party scripts. Everything Tapback knows about a reader is a random ID that their own browser made up, and even that is hashed before it touches storage. This page walks through exactly what exists, where it lives, and how long.
What a "reader" is
When the reaction bar first loads in someone's browser, the embed generates a random UUID and keeps it in the site's own localStorage (the key is tapback:aid). That ID is the reader's whole identity:
- It never leaves your site's storage scope. localStorage is bound to your origin by the browser, so the same person reading two different Tapback-powered sites carries two unrelated IDs. Tapback's servers can't connect them either — there is nothing shared to join on.
- The server stores a hash of it. On each session the ID is HMAC-hashed with a server-side key and truncated; that hash is what room storage keys tap counts and toggle state on. The raw ID appears in the session request and nowhere at rest.
- The reader can destroy it any time. Clearing site data, or reading in a private window, produces a fresh ID — to Tapback that's a brand-new reader. This is also why the uniques count can occasionally count the same human twice: the ID is the person, and the person controls the ID.
- If localStorage is blocked (some private modes, strict browser settings), the embed falls back to an ID held in page memory. Reactions still work for the visit; nothing persists on the reader's machine at all.
There is deliberately no durable identifier beyond this. A reader who only watches leaves no per-reader rows behind — server-side storage is keyed on the hash only when they tap something.
What Tapback stores
Per page (a "room"), four kinds of rows:
- Counts — the aggregate number per reaction kind, kept for the life of the page.
- Per-reader rows, keyed by the hash — your tap count per kind (so "you clapped ×12" survives a reload) and toggle state. These rows carry a hashed ID, a kind, a small integer, and a timestamp. No IP, no user agent, no URL trail.
- Deduplication receipts — the last acknowledged tap per reader, so a flaky connection can't double-count; swept on the room's next housekeeping pass, about two minutes later.
- Per-IP day counters — how many taps and new reactors a truncated IP hash produced on that page today (the rate limits below); swept on the room's housekeeping pass once three days old.
The long-term record — the daily rollups behind your analytics, exports, and the counts API — is aggregate only: totals and distinct-reader counts per page, per reaction, per day. Per-reader rows never leave the room they were written in, and deleting them never changes a published count (the distinct-reader counts are monotonic; see verifiable counts).
IP addresses are HMAC-hashed and truncated to an 8-byte fragment the moment a session is minted, and the raw address is discarded. The fragment rides inside the session token (which expires after 10 minutes) and lands in the per-IP day counters above, which are swept on the room's housekeeping pass once three days old. Raw IPs are never written to storage.
What loads on a reader's page
The embed is one script tag from tapback.party, and from the reader's browser it talks to exactly one origin: api.tapback.party. That covers the session request, the live WebSocket, and custom reaction icons (served at immutable content-hashed, cookie-less paths). The session request carries three things: your publishable key, the page's entity ID, and the reader's random ID. Built-in icons are baked into the script itself, and icons picked from the Iconify library are fetched and stored by Tapback when you configure them — readers never contact a third party.
No fonts, no CDNs, no analytics beacons, no pixel. The loader is ~9KB gzipped and defers its live core (the WebSocket client and animation engine) until the reaction bar is on screen — for an inline bar down the page, a reader who never scrolls to it never opens a socket.
Readers also get a control of their own: by default the bar carries a mute control — on the free tier it's in the Tapback mark's menu (Mute reactions) — which stops the falling overlay on that site and is remembered locally (see the JS API for the programmatic version).
Abuse handling without CAPTCHAs
Tapback never challenges a reader — no CAPTCHA, no puzzle, no "verify you are human" wall in front of a tap. Abuse is absorbed silently instead:
- each reader has a token bucket (a few taps per second, with a small burst allowance) and a per-reaction lifetime cap, enforced in the room;
- each IP address is bounded per page per day — at most 10 new reactors and 500 taps — using only the truncated IP hash, so churning through fresh IDs doesn't mint fresh people;
- each Tapbar has a daily reaction budget, so a flood hits your Tapbar's ceiling instead of your bill — and even a frozen page keeps serving live viewing, since watching costs nothing.
A limited reader's taps still animate locally; the counter just stops moving. The design goal is that a real audience never notices any of this exists. The same limits are what make the numbers worth publishing — they're the bound behind the uniques and attestation story.
Where the data lives
Everything runs on Cloudflare: room storage in Durable Objects, rollups in D1, icons in R2. Tapbars can be created with EU hosting, which pins the Tapbar's rooms and its rollup database to EU-located infrastructure — pick it at creation, and on Plus or Pro move it between regions later. It's offered as a locality choice: your reaction data stays resident in the EU and the live path runs close to a European audience.
Your data and your readers' requests
You own the counts. The export page covers one-click JSON/CSV snapshots, a curl-able backup endpoint, and a static archive that keeps rendering your counts with zero calls to Tapback. Ask us to delete your account and the account record, Tapbar configurations, keys, and support history go with it; the aggregate counts, and the hashed per-page reaction rows they were built from, are retained — with the Tapbar's config and keys gone, those hashes join to nothing.
If a reader asks you what Tapback holds on them, the factual answer is short: a hashed random ID with tap counts attached, joined to nothing else. They can sever it themselves by clearing your site's data in their browser, which orphans the hash — the aggregate counts they contributed to are retained, their association with them is gone.
For a privacy policy or records-of-processing entry, the concrete facts are the ones above: identifier is a first-party localStorage UUID created when the bar loads, pseudonymized server-side by keyed hash; no cookies are set; no data is shared with third parties beyond Cloudflare as the hosting provider; IPs are used for rate limiting and stored only as truncated hashes — in tokens that expire after 10 minutes and in day-bucketed counters kept for roughly three days. Whether and how Tapback appears in your policy is between you and your counsel — but this page is the complete inventory, and if you need something it doesn't answer, ask.