Effective date: 2026-05-23 — Last updated: 2026-07-15
Reading Pal is a reading tracker and pomodoro timer app operated by Darren Lin (solo developer, Taiwan). This Policy explains what personal data we collect when you use Reading Pal on Android or iOS, why we collect it, who we share it with, and the rights you can exercise over it.
This Policy applies to the Reading Pal mobile application and the backend API that supports it. It does not apply to third-party services you may link (e.g., Google) — those are governed by their own privacy policies.
| Field | Source | Purpose |
|---|---|---|
| Email address | You (at registration) or Google OAuth | Account identity, login, account-recovery emails |
| Display name | You or Google OAuth | Shown in-app |
| Avatar URL | Google OAuth (if applicable) | Profile picture |
| Password hash (bcrypt) | You (email+password registration only) | Authentication — we never see or store your plaintext password |
| Locale + timezone | Set at registration | UI localisation; correct notification delivery times |
sub), email, email_verified flag, and the raw id_token payload for verification audit. Retained at most until account deletion.| Field | Purpose |
|---|---|
| Anonymous device ID (UUID generated on-device) | Ties push tokens and sessions to a device without linking to your phone's hardware identifier or IMEI |
| FCM push token (Firebase Cloud Messaging) | Lets us deliver push notifications to your device |
| App version + OS version | Crash diagnostics; compatibility checks |
Each request to our backend logs: - IP address (used for rate-limiting, security, and abuse detection — rotated automatically after 30 days) - HTTP method, URL path, response status code, response latency, request ID
We do not log request bodies in production.
We maintain an audit log for four event types only: deletion_requested, deletion_cancelled, deletion_executed, and export_requested. For each event we record:
- Timestamp
- Event type
- Numeric user ID
- IP address, device ID, and user-supplied deletion reason (if provided, capped at 2,000 characters) — these metadata fields are set to NULL at the moment of hard delete; only the event type, timestamp, and numeric user ID survive beyond that point
These records survive your account deletion and are retained indefinitely for compliance verification — to demonstrate that we honored your deletion or export request. After hard delete, the surviving records are fully de-identified: they contain only event type, timestamp, and a numeric user ID that no longer maps to any person (the account itself is gone). They hold no PII and no reading content.
When the app crashes or the backend encounters an error, we send a report to Sentry containing: - Stack trace - Your numeric user ID (not your email or display name) - App version and environment label - Request URL path (query strings stripped)
We explicitly do not send: your email address, display name, IP address, request body, cookies, refresh tokens, or any free-text note content.
Sentry retains these reports for 7 days (free tier default). Sentry data is hosted in the EU region (de.sentry.io). See Section 4 for Sentry's processor details.
We use Firebase Analytics (a Google service) to understand aggregate usage patterns and improve the app. The events and parameters we send are limited strictly to the following:
| Event / Field | Parameters | Purpose |
|---|---|---|
screen_view |
screen_name, screen_class |
Screen navigation and drop-off funnel. Values are PII-free route literals (/library, /library/[bookId], /profile/shop, etc.); dynamic IDs are replaced with literal placeholders |
sign_up, login, guest_start |
None | Auth funnel conversion rates. No email or account data attached |
add_book |
category (e.g., fiction) |
Reading funnel entry and content-type preference. Category code only — no book title, content, or ISBN |
start_reading_session |
None | Reading funnel: session started. No book ID or title |
complete_reading_session |
duration_bucket (<15, 15-30, 30-60, 60+ minutes) |
Reading funnel: session completed and engagement-time distribution. Coarse bucket only — no precise duration in seconds |
view_subscription, begin_purchase, purchase_success |
product_id (SKU constant) |
Monetization funnel. product_id is a static SKU string, not personal data — no purchase token, amount, or payment details |
view_shop, redeem_item |
item_id (item constant) |
Shop funnel. item_id is a static constant — no quantity or balance |
rewarded_offer_shown, rewarded_started, rewarded_earned |
None | Rewarded ad funnel: impression → started → completed |
Firebase built-in: engagement_time, session_start, first_open |
Automatic | Engineering metrics automatically collected by the Firebase SDK — we do not write these manually |
What we do not send to Firebase Analytics: your email address, display name, book titles, note content, reading timestamps, precise reading durations, ISBNs, purchase tokens, or any free-text content.
Firebase Analytics also automatically associates events with an App Instance ID (a Firebase-generated per-install identifier, resettable by clearing app data or reinstalling) and, on Android, the Android Advertising ID (a resettable device-level ID managed by Google). These are collected by the Firebase SDK itself; we do not attach them manually.
Firebase Analytics data is processed by Google under Google's privacy terms. See Section 4.
| Purpose | Data used |
|---|---|
| Provide the service — sync library across devices, track sessions, compute reading statistics | Sections 2(a), 2(c), 2(h), 2(i) |
| Authentication and account security (token rotation, session invalidation) | Sections 2(a), 2(b), 2(d) |
| Push notifications (strictly opt-in per type — see Section 10) | Section 2(d) FCM token |
| Crash diagnostics and service reliability | Section 2(g) |
| Security — rate-limiting by IP and device, fraud detection on auth flows, defense against credential stuffing | Sections 2(d), 2(e) |
| Demonstrating compliance with deletion / export requests | Section 2(f) |
| Analytics — understanding aggregate usage patterns and improving the app | Section 2(j) |
Email verification (email/password registration): When you register using email and password, we send a verification email containing a 6-digit code via Resend to confirm you own the email address. Signing in with Google does not trigger this flow.
In-app advertising (Google AdMob): Reading Pal displays ads served by Google AdMob (Android only; no ads on iOS). Three ad formats are used: a banner at the bottom of the book library list, a full-screen interstitial shown after a reading session of 13 minutes or more, and a rewarded ad that users can choose to watch in exchange for in-app points.
Data shared with AdMob for ad serving, measurement, and fraud prevention: the Android Advertising ID (AAID — a resettable device-level identifier managed by Google) and contextual/technical signals (app and device metadata). The AAID is already collected by the Firebase Analytics SDK (see Section 2(j) above); AdMob reuses the same identifier for advertising purposes.
For rewarded ads, we use AdMob's server-side verification (SSV): a pseudonymous numeric user ID and a custom data string are sent to AdMob solely to verify reward eligibility. This is not used for ad targeting.
We do not share with AdMob or any advertiser: your email address, display name, account data, book library, or reading content.
EEA / UK users: Google's User Messaging Platform (UMP) consent flow runs before ads are loaded. If you decline consent or applicable law requires it, only non-personalised ads are shown (or no ads at all). Ads are not loaded before consent is obtained (fail-closed).
Opt-out: Android Settings → Privacy → Ads → Reset or delete your Advertising ID.
| Provider | Role | Data Shared | Region |
|---|---|---|---|
| Google (OAuth) | Sign in with Google | Standard OpenID Connect claims (sub, email, name, picture) | Global |
| Google Firebase Cloud Messaging (FCM) | Push notification delivery | FCM registration token; push notification title and body | Global |
| Google Firebase Analytics / Google Analytics | Usage analytics | App interaction events and parameters listed in Section 2(j); App Instance ID; Android Advertising ID (Android, SDK-automatic) | Global |
| Google Play | App distribution and licensing | Standard install metadata (no reading data) | Global |
| Sentry (Functional Software, Inc., US; data hosted EU) | Crash and error monitoring | See Section 2(g) — stack trace, numeric user ID, version, path | EU |
| Resend (Resend, Inc., US; sending region set to EU — Ireland) | Transactional email delivery | Email address + verification code content, only when you register with email/password | EU (Ireland) |
| Apple / APNs | iOS push delivery (only when iOS version launches — currently Android-only) | FCM token equivalent (APNs device token) + push payload | Global |
| Google AdMob | In-app advertising (Android) | Android Advertising ID (AAID) + contextual/technical signals; rewarded-ad SSV also sends a pseudonymous numeric user ID for reward verification (not for targeting) | Global |
We do not sell personal data to any third party, ever.
All your account, library, and reading data is stored in DigitalOcean's Frankfurt (EU) data center (region: FRA1). EU users' data does not leave EU territory.
Sentry crash reports also use Sentry's EU region (de.sentry.io).
Some sub-processors (Google, AdMob) operate globally and may move data between regions for service reliability per their own privacy terms.
You can exercise most rights directly in the app without emailing us.
| Right | How to exercise |
|---|---|
| Access | Settings → Account → "Export my data" — produces a full JSON export immediately |
| Rectification | Settings → Profile — edit display name and other profile fields directly |
| Erasure (deletion) | Settings → Account → "Delete account" — starts a 30-day grace period. Log back in during those 30 days to cancel. After 30 days, all personal data is permanently deleted from our database (a de-identified audit record is retained for compliance per Section 2(f)). |
| Portability | Same JSON export as Access |
| Restriction | Email us (Section 12) |
| Object to marketing | Settings → Notifications → "Marketing push" — toggle off (default: off) |
EU / EEA users: you also have the right to lodge a complaint with your national supervisory authority (e.g., your country's data protection authority).
Taiwan users: rights are grounded in the Personal Information Protection Act (個人資料保護法, PIPA).
We will respond to requests within 30 days of receipt.
Reading Pal is not directed at children under 13. We do not knowingly collect personal data from users under 13. If you believe a child has created an account, email us at tn08869679@gmail.com and we will delete the account.
Reading Pal is a mobile app — there are no web cookies. We use these identifiers:
| Identifier | Type | Purpose | Resettable? |
|---|---|---|---|
| Device ID | Anonymous UUID, generated locally on first launch | Sessions, push routing — no link to hardware ID | Yes — uninstalling the app generates a new one |
| FCM token | Assigned by Firebase | Push notification delivery | Yes — Firebase refreshes it periodically; also cleared on logout |
| Firebase App Instance ID | Assigned by Firebase Analytics SDK on install | Associates analytics events with a single install | Yes — cleared when you clear app data or reinstall |
| Android Advertising ID | Google (collected automatically by Firebase Analytics SDK on Android; also used by AdMob for ad serving) | Analytics event association; AdMob ad serving & measurement | Yes — Settings → Privacy → Ads → Reset Advertising ID |
| Data category | Retention period |
|---|---|
| Account, library, reading sessions, settings, achievements, points | Active until you delete your account + 30-day grace period, then hard-deleted |
| Audit log (deletion/export event records — no personal reading content) | De-identified record (event type + timestamp + numeric user ID that no longer maps to any person) retained indefinitely for compliance verification; IP, device ID, and user-supplied reason are NULL'd at hard delete |
| Server access logs (IP, request metadata) | 30 days, then automatic rotation |
| Crash reports (Sentry) | 7 days (Sentry free tier default) |
| Refresh token records | Until the token expires or is explicitly revoked |
| Firebase Analytics event data | Governed by Google's retention settings (default 2 months for user-level data; aggregate reports may be retained longer by Google). See Google's data retention documentation. |
Marketing push notifications default to OFF. You opt in explicitly: Settings → Notifications → "Marketing push". You can turn off any time in the same place.
We do not send marketing emails.
Material changes (new data category, new sub-processor, expanded use of existing data) are announced via an in-app banner 30 days before taking effect. The updated version is dated.
Minor edits (typo fixes, clarifications that don't affect your rights) take effect immediately without prior notice.
Pre-disclosed upcoming changes:
Material changes (new data categories, new sub-processors, or expanded use of existing data) will be announced via an in-app banner at least 30 days before taking effect.
For privacy questions, complaints, or requests we cannot handle in-app:
We aim to respond within 30 days.
| Version | Date | Summary |
|---|---|---|
| 1.0 | 2026-05-23 | Initial release |
| 1.1 | 2026-06-14 | Added Section 2(j) Firebase Analytics disclosure; updated Sections 3, 4, 8, 9, 11 accordingly |
| 1.2 | 2026-06-23 | In-app advertising (Google AdMob) updated from planned to current; updated Sections 3, 4, 8, 11 accordingly |
| 1.3 | 2026-06-23 | Audit log retention clarified as de-identified and indefinite (removed unimplemented 6-month deletion promise); data export timing corrected to immediate |
| 1.4 | 2026-07-15 | Added Resend as sub-processor (email/password registration verification emails); disclosed email verification flow |