Payd – Paycheck Planner
Bundle ID: com.paydplanner.app
Document version: 1.6 · Last updated: July 19, 2026
Status: Living document — updated whenever Payd's data handling changes
How to read this document
Every factual claim below was verified directly against Payd's source code or against the current, documented behavior of a named third-party service, on the date this document was last updated. Where a claim depends on a third party's own policies (RevenueCat, Apple, Google), we link to their documentation rather than restate it, because those policies can change on their own schedule.
This document does not use the phrase "bank-level encryption" or similar marketing shorthand. Where we describe encryption, we name the exact algorithm. Where we describe a limitation, we state it plainly rather than omit it.
If anything in this document is ever found to be inaccurate, we will correct it and note the correction in the changelog at the bottom of this page.
1. Executive Summary
Payd is a paycheck-based budgeting app that does not connect to your bank. There is no bank login, no account aggregator (Plaid, Yodlee, Finicity, or similar), and no Payd server that stores your financial data. Every bill, balance, paycheck, and goal you enter is stored locally on your device, using the operating system's standard local storage.
Three narrow categories of network activity exist in the app, described in full in Section 5. Outside of those three, Payd has no way to see, transmit, or store your financial information, because the app has no backend capable of receiving it.
This architecture is a tradeoff, not a marketing claim. It means Payd cannot auto-categorize transactions from your bank the way Copilot or Monarch can, and it means there is no cloud sync between your devices unless you do it yourself via encrypted backup (Section 8) or the optional Partner Sync feature (Section 6). What it buys in return: there is no Payd database to breach, because there is no Payd database.
2. Design Philosophy
Most budgeting apps ask you to link a bank account through an aggregator so the app can pull transactions automatically. That convenience comes with a tradeoff: your login credentials, transaction history, and account balances pass through and are stored by a third party you didn't choose and likely haven't read the terms of.
Payd takes the opposite default. You tell the app what you earn and what you owe. In exchange for a small amount of manual entry — reduced by features like PDF bank-statement import (Section 6), which detects recurring bills from a statement you choose to upload once, without ever transmitting that statement anywhere — your financial picture never has to leave your phone.
This is a deliberate product decision, not a limitation we're working around. It is why the architecture described in this document is possible at all.
3. What Data Exists, and Where It Lives
Everything you enter into Payd is stored using your device's local app storage (AsyncStorage, the standard React Native/Expo local storage layer used by the majority of mobile apps). Nothing here is a Payd server — it is a set of files on your device, inside a storage area the operating system reserves for Payd and does not share with other apps.
| Data category | Storage key | Ever leaves the device? |
|---|---|---|
| Pay schedule, bills, balances, spend log, budgets | payd_data |
No |
| Tax / paystub entries | payd_taxes |
No |
| Investment portfolio holdings | payd_portfolio |
No |
| Savings goals & contributions | payd_savings |
No |
| Theme & appearance settings | payd_theme |
No |
| Subscription status (Pro/free, trial state) | payd_entitlements |
Anonymized purchase events only — see Section 5 |
| Cashflow chart starting balances | payd_starting_balances |
No |
| Payday confirmation state | payd_payday_confirmation |
No |
| Home screen widget data | payd_widget_data |
No |
| Daily bill-checklist state | payd_checklist_* |
No |
| Stock-quote provider API keys (optional, user-entered) | payd_api_keys |
Only to that provider, as part of your own quote requests — see Section 6.2 |
| Stock-quote provider API usage counters | payd_api_usage |
No |
| Cached historical-return calculations (Investment Calculator's opt-in past-performance projection) | payd_hist_returns |
No — this is a local cache of results already fetched under Section 5.1 |
| App Lock on/off preference (optional Face ID / passcode gate) | payd_app_lock |
No — and note this stores only the on/off setting. Biometric data and your device passcode never reach Payd at all; the operating system performs the authentication and returns only a yes/no (Section 7) |
| App Store review-prompt state | payd_review_state |
No |
| Last local backup timestamp | payd_last_backup_at |
No |
Nothing in this table is transmitted to Payd, because Payd has no server to receive it. There is no account system, no login, and no user ID that identifies you to us — we don't have a "your data" to look up, because we never receive it.
4. What "Local Only" Actually Means, Technically
To be precise about a claim that's easy to overstate: "local only" means Payd's own code never makes a network request containing your financial data. It does not mean your data is invisible to the operating system, to other software running with elevated privileges on a compromised device, or to anyone with unlocked physical access to your phone.
Local storage on iOS and Android benefits from the same OS-level protections as any other app's local data — iOS Data Protection and Android's file-based encryption, both of which are active on a device protected by a passcode, PIN, or biometric lock. Payd does not layer additional encryption on top of this for day-to-day app data, the same way most note-taking or to-do apps don't. If your device itself is compromised (jailbroken, rooted, or unlocked without a passcode), local app storage becomes more exposed — this is true of Payd and true of nearly every app on your phone that isn't using a hardware-backed secure enclave for every field, which comes with real usability tradeoffs most apps (including Payd, today) don't make.
Where we do apply real encryption — your optional local backup file — is described in Section 8, with the exact algorithm named.
5. The Three Things That Touch the Network
Payd makes network requests in exactly three circumstances. Nothing else in the app calls out to the internet.
5.1 Stock price lookups (Portfolio tab, optional)
If you add a stock holding to the Portfolio tab, Payd fetches a current price by sending the ticker symbol (e.g. AAPL) to one of three providers, tried in order: Yahoo Finance's public quote endpoint, then Finnhub, then Alpha Vantage (the latter two only if you've supplied your own free API key for them). This request contains only the ticker symbol — no account information, no identifier tied to you, nothing from any other part of the app. If you never open the Portfolio tab or add a holding, this request never fires.
One related request exists outside the Portfolio tab: when you enter a Finnhub or Alpha Vantage API key in Settings and tap "Test," Payd makes a single validation request to that provider containing a fixed test ticker (AAPL) and the key you just entered — nothing else. This is the same class of request as a quote lookup, fires only when you explicitly test a key, and goes only to the provider the key belongs to.
The Investment Calculator inside the Portfolio tab includes an opt-in checkbox, off by default: "Base on my holdings' past performance." If you tick it, Payd fetches up to 5 years of monthly closing prices for each of your holdings' tickers — from Yahoo Finance's chart endpoint, then Alpha Vantage if you've supplied your own key (Finnhub is not used for this request; its free tier does not offer historical price series) — and computes each holding's annualized return, then weights those returns by position size to fill in the calculator's Annual Return field. This is the same ticker-only request class as the rest of this section: no account information, no identifier tied to you, nothing else from the app. It fires only when you tick the box, never automatically and never in the background, and the results are cached locally for 7 days (payd_hist_returns, Section 3) so reopening the calculator doesn't re-fetch. Unticking the box unlocks the Annual Return field for manual entry and has no further network effect. The calculator always shows the result alongside a disclaimer: "Past performance does not guarantee future results — this is an estimate for planning, not investment advice."
5.2 Subscription purchases (RevenueCat)
Payd uses RevenueCat to process App Store and Google Play subscription purchases and to verify subscription status. This is described in full in Section 6.1.
5.3 In-app rewarded "ads" (currently simulated, not live)
Payd includes an option to earn temporary Pro access by watching a rewarded ad. As of this document's last update, this feature is implemented as a 3-second in-app simulation — no third-party ad network is integrated, and no ad SDK is present in the app's dependencies. To be equally plain about intent: this is a planned feature, not a leftover. The reward flow is already built into the app, and integrating a real ad network is an intended future step — not a hypothetical. If a real ad network is added in a future version, this document will be updated before that version ships, and the specific network, the data it collects, and any consent flow (including Apple's App Tracking Transparency, currently not triggered because no ad SDK is present) will be disclosed here by name.
Everything else — bill entry, balance tracking, the cashflow chart, savings goals, tax estimates, PDF bank-statement parsing, portfolio screenshot import (Section 6.5), the Today screen, notifications, themes — runs entirely offline and makes zero network calls.
6. Third-Party Services, in Full
Payd integrates exactly one paid third-party service (RevenueCat) and three optional data sources (Yahoo Finance, Finnhub, Alpha Vantage). There are no analytics SDKs, no crash-reporting SDKs, and no advertising SDKs currently active in the app. This was verified by inspecting the app's full list of dependencies — there is no Sentry, Firebase, Amplitude, Mixpanel, Segment, or comparable service installed. One planned exception deserves stating here, up front, rather than only in a subsection: ad-supported access is on Payd's roadmap (Section 5.3). No advertising SDK is present today, but when one is added, this document will name the network, describe exactly what data it collects, and document any consent flow — before that version ships, not after.
6.1 RevenueCat (subscription billing)
Payd uses RevenueCat to manage App Store and Google Play subscriptions. Payd does not pass RevenueCat your name, email, or any identifier from within the app. Because Payd never calls RevenueCat's logIn() method or sets a custom user ID, RevenueCat's SDK automatically generates a random, anonymous identifier (documented by RevenueCat as prefixed $RCAnonymousID:) the first time the app configures itself, and that identifier is all RevenueCat associates with your purchase activity.
RevenueCat receives: the anonymous ID, and purchase/subscription lifecycle events (purchased, renewed, canceled, refunded) relayed from Apple or Google. RevenueCat does not receive: your bills, balances, savings goals, or any other data Payd stores — RevenueCat's SDK has no access to that data and Payd never sends it. RevenueCat's own privacy practices are documented at revenuecat.com/privacy.
6.2 Yahoo Finance, Finnhub & Alpha Vantage (stock price lookups)
Described in Section 5.1, including the Investment Calculator's opt-in historical-returns fetch. These receive only a ticker symbol, on-demand — when you view the Portfolio tab, when you test an API key, or when you tick "Base on my holdings' past performance" in the Investment Calculator (Yahoo Finance and Alpha Vantage only for that request; Finnhub does not participate). If you optionally supply your own Finnhub or Alpha Vantage API key (used to raise your personal rate limit for lookups), that key is stored locally on your device and sent only to that provider, as part of your own request to their service.
6.3 Apple / Google push notification infrastructure — not used
Payd's bill and payday reminders are scheduled entirely on-device using each platform's local notification APIs. Payd never requests a remote push token from Apple or Google, and consequently never has one to send anywhere. This means Payd has no mechanism to send you a notification remotely, at all — every notification you receive was scheduled by your own device, in advance, based on data that never left it.
6.4 Partner Sync (optional, user-initiated)
Partner Sync lets two people compare budgets without either party's phone talking to a server — because there is no server. When you tap "Share," Payd generates a snapshot of your data (listed exactly below) and hands it to the operating system's native share sheet. From there, you choose the channel — Messages, AirDrop, email, or anything else installed on your phone. Payd has no visibility into what happens after that; the transmission is entirely between your device and the app you picked.
The snapshot contains: your self-chosen display name, safe-to-spend amount, next payday date, total debt, bill names and amounts (for recurring monthly bills only), and checking/savings totals. It does not contain account numbers, institution names (beyond what you've manually typed as a bill or account label), or transaction-level history.
Important limitation, stated plainly: this snapshot is base64-encoded for compact transport, not encrypted. Anyone who intercepts the encoded string before it reaches your partner could decode and read it. In practice, its confidentiality depends entirely on the security of the channel you choose to send it through — iMessage and most modern messaging apps use their own end-to-end encryption for the message itself, which is what actually protects this data in transit, not anything Payd does to the payload. We recommend sharing it only through a channel you already trust with sensitive information.
6.5 Portfolio screenshot import (optional, on-device)
The Portfolio tab's bulk-add sheet can pre-fill holdings from a screenshot of your brokerage's positions screen, so you don't have to retype each ticker. Precisely what happens, in order:
- You pick an image using the operating system's own file/photo picker. This uses the system picker UI, which does not require Payd to hold a photo-library or camera permission — Payd receives only the single image you chose.
- The image is copied into Payd's private cache and read by Google ML Kit's on-device text recognition (@react-native-ml-kit/text-recognition, bundled model). Recognition runs entirely on your device; ML Kit's on-device APIs make no network request and the image is never uploaded anywhere.
- The recognized text is parsed locally into candidate tickers, share counts, and average costs, shown to you for review and editing. Nothing is saved until you explicitly tap "Add all," and then only the rows you kept — never the image or its raw text.
- The cached image copy is deleted the moment recognition finishes — success or failure — in the same code path (
finallyblock incomponents/BulkAddSheet.tsx, checkable in source). Payd retains no copy, thumbnail, or OCR text.
ML Kit is an on-device library, not a service: it receives no identifier, sends nothing, and is subject to the same "no analytics, no tracking" verification as the rest of the dependency list (Section 6).
7. Permissions Payd Requests
| Permission | Platform | Why |
|---|---|---|
| Notifications | iOS & Android | Local bill and payday reminders, scheduled on-device |
| Vibrate | Android | Haptic feedback on notification delivery |
| Receive boot completed | Android | Re-schedules local notifications after a device restart (notifications don't survive a reboot otherwise) |
| Schedule/use exact alarm | Android | Ensures payday/bill reminders fire at the exact time set, not "roughly around" it |
| Face ID | iOS | Optional App Lock (off by default). Authentication is performed entirely by iOS; Payd receives only a success/failure result — never biometric data, never your passcode |
| Biometric / fingerprint | Android | Same as above, via Android's BiometricPrompt. These manifest entries are added by the authentication library; Payd stores no PIN of its own — the passcode fallback is your device's own lock screen |
Payd explicitly blocks itself from requesting the following, even though some app templates include them by default: contacts (read/write) and call log. There is no feature in Payd that would use them, so the app declares them blocked at the manifest level rather than simply not asking.
Payd does not request: camera access (Partner Sync uses text paste via the share sheet, not a QR scanner), location, microphone, or contacts.
8. Data You Control
8.1 Local backup & restore
You can export a full backup of your Payd data as a file, saved via the OS share sheet — typically to Files/iCloud Drive on iOS or your Android file system, wherever you choose to save it. You control where that file lives; Payd has no involvement once it's saved.
You may optionally protect this backup with a passcode. When you do, Payd encrypts the file using AES-256 in CBC mode, with a key derived from your passcode via PBKDF2 with 10,000 iterations of SHA-256, and a freshly generated random salt and initialization vector on every export. There is no way to recover an encrypted backup if you forget the passcode — Payd never stores it, transmits it, or retains any way to reverse the derivation.
8.2 Full local reset
Settings → Reset All Data permanently erases every category of data listed in Section 3, from every storage location Payd uses on your device — not a subset. This is implemented as a full clear of the app's local storage, not a list of individually-remembered keys, specifically so that no future feature can accidentally be left out of what "reset" means.
One documented exception: RevenueCat's own anonymous identifier (Section 6.1) lives in RevenueCat's native SDK cache, outside the storage area Payd controls, and RevenueCat does not provide a supported way to clear it without a full app deletion. It is not linked to any of your Payd data and carries no information beyond "an anonymous device once configured this app" — but for full precision, a local reset does not remove it. Deleting the app does.
8.3 Deleting the app
After a full reset, Payd offers to help you remove the app itself. What actually happens differs by platform, and we describe both honestly rather than implying parity:
- Android allows an app to trigger the operating system's own uninstall-confirmation dialog — the same one shown when you drag an app icon to "uninstall." Payd uses this real, standard Android capability; you still confirm the deletion yourself in the OS dialog.
- iOS provides no equivalent API. No app on iOS, including Payd, can delete itself — this is an Apple platform restriction, not a Payd limitation. Payd deep-links you directly to its own Settings page, one tap from "Delete App," which you then tap yourself.
9. How This Compares Structurally to Bank-Linked Budgeting Apps
This is a factual, structural comparison — not a claim that one approach is universally better. Bank-linked apps offer real conveniences Payd does not: automatic transaction import, AI-assisted categorization, and a live view of exactly what cleared your account. Those features require your bank credentials or account data to pass through an aggregator (commonly Plaid, Yodlee, or Finicity) and then be stored by the app's own backend to power search, categorization, and sync across your devices.
| Typical bank-linked app | Payd | |
|---|---|---|
| Requires bank login | Yes, via an aggregator | No |
| Stores transaction history on a server | Yes | No server exists |
| Auto-categorizes spending via AI | Often | Not currently a feature |
| Data breach exposes | Aggregator + app's own database | Nothing — no database exists |
| Works with no internet connection | No (needs to sync/refresh) | Yes, entirely |
| Cross-device sync | Automatic, server-mediated | Manual (Section 8.1) or Partner Sync (Section 6.4) |
If your priority is minimizing how many parties ever see your bank credentials or transaction history, the structural difference above is real and verifiable — not a matter of trusting either company's stated policy, but a difference in what's technically possible to breach.
10. Known Limitations
Stated directly, without hedging:
- Local storage is not independently encrypted by Payd. It relies on your device's own lock-screen protection (Section 4). A jailbroken/rooted device, or one unlocked without a passcode, offers reduced protection — this is a real limitation we may address in a future version, and we will update this document when and if we do. The optional App Lock (Section 7) narrows one path to this data — someone holding your unlocked phone can no longer open Payd — but to be precise about what it is: App Lock gates the app's interface. It is not storage encryption, and it does not change what an attacker with filesystem-level access to a compromised device could read.
- Partner Sync payloads are not encrypted — only encoded for transport. Their confidentiality depends on the security of the channel you send them through (Section 6.4).
- RevenueCat's anonymous identifier persists after a local data reset, until the app itself is deleted (Section 8.2).
- True self-deletion is impossible on iOS by Apple's own design — Payd cannot remove itself from your device without your final manual tap (Section 8.3).
- The "watch an ad" Pro-access feature is currently simulated, not backed by a live ad network (Section 5.3) — and it is a planned feature, not a discarded experiment. When a real ad network is integrated, it will collect data the rest of the app does not; the network and its data practices will be named here before any ad-enabled version ships.
11. Security Practices We Follow
Payd has no backend server, no database, and no API endpoints of its own — which means the majority of a standard web-app security checklist (row-level database security, server-side input validation, rate limiting, CORS configuration, SQL injection prevention) does not apply, because there is no server-side surface for those risks to exist on. This isn't a loophole; it's the direct consequence of the architecture described in Section 1.
What we do apply, given that architecture:
- No analytics, crash-reporting, or advertising SDK is bundled with the app (verified against the app's full dependency list at time of writing).
- No API key with the ability to spend money or access sensitive data is embedded in the app in a way that would let a third party drain an account we control — the only paid API integration (RevenueCat) uses a client-safe key by design, the same class of key RevenueCat documents as intended for client-side use.
- The one feature that previously used a weak, home-rolled "encryption" scheme (Section 8.1's backup passcode) has been replaced with industry-standard AES-256 with PBKDF2 key derivation.
- The one feature that previously left partial data behind after a "reset" (Section 8.2) now performs a verified full clear of local storage.
- The optional App Lock delegates authentication entirely to the operating system (Face ID / fingerprint / device passcode). Payd implements no PIN of its own, so there is no Payd-stored credential to leak, forget, or brute-force — the only thing Payd stores is whether the lock is on (
payd_app_lock, Section 3).
12. Changelog
v1.6 — July 19, 2026 New feature disclosed before ship, per the living-document policy:
- App Lock added (Sections 3, 7, 10, 11). An optional, off-by-default toggle in Settings → Privacy requires Face ID, fingerprint, or the device passcode to open Payd. Authentication is delegated entirely to the operating system — Payd receives only a success/failure result, never biometric data or the passcode, and stores no PIN of its own. The single new stored value is the on/off preference (
payd_app_lock), cleared by Reset All Data like everything else. New OS permission entries: Face ID usage description (iOS) and biometric/fingerprint manifest entries (Android). If the device has no lock screen at all, App Lock fails open rather than locking you out of your own data, and cannot be enabled until one is set. Known Limitation #1 updated to state precisely what App Lock is (an interface gate) and is not (storage encryption). Not available on web builds.
v1.5 — July 19, 2026 New feature disclosed before this document's first publication:
- Investment Calculator's opt-in historical-returns projection added (Sections 3, 5.1, 6.2). An off-by-default checkbox — "Base on my holdings' past performance" — lets the calculator's Annual Return field be computed from your actual holdings' history instead of a manual guess. Ticking it fetches up to 5 years of monthly closing prices per ticker (Yahoo Finance, then Alpha Vantage if you've supplied a key), computes each holding's annualized return, and weights the result by position size. Same ticker-only request class as the existing price lookup in Section 5.1 — fires only when the box is ticked, never automatically, and results cache locally for 7 days (
payd_hist_returns, added to Section 3's storage table). The calculator always displays the disclaimer: "Past performance does not guarantee future results — this is an estimate for planning, not investment advice."
v1.4 — July 19, 2026 Transparency correction, prompted by our own review — no code or data handling changed:
- Rewarded-ad intent disclosed up front. Previous versions described the rewarded-ad feature as "currently simulated" but left the planned nature of real ad integration easy to miss. Sections 5.3, 6, and 10 now state plainly that ad support is a planned feature with its reward flow already scaffolded in the app, and repeat the commitment that any ad network will be named here — with its data collection and consent flow — before an ad-enabled version ships. Marketing copy on this site was softened to match ("no analytics or tracking SDKs" rather than implying ads were never coming).
v1.3 — July 13, 2026 New feature disclosed before ship, per the living-document policy:
- Portfolio screenshot import added (Section 6.5). Holdings can be pre-filled from a brokerage screenshot using Google ML Kit's bundled, on-device text recognition. No network request is involved, the image is picked via the OS's own picker (no photo-library permission), and the cached copy is deleted immediately when recognition ends — the deletion lives in the same
finallyblock as the recognition call, so no code path can skip it. A bulk manual-entry sheet (ticker, shares, average price) ships alongside it; both are Pro features inside the Portfolio tab.
v1.2 — July 8, 2026 Four additions/corrections, prompted by a full source re-audit (PRIVACY_AUDIT.md v2.0.0):
- API-key validation request disclosed. Testing a Finnhub or Alpha Vantage key in Settings makes one validation request to that provider, containing a fixed test ticker (
AAPL) and the user's own key. Same data class as a quote lookup; previously undocumented. Section 5.1 updated. payd_api_keysadded to the storage table (Section 3). User-entered provider API keys were already described as stored locally in Section 6.2, but the table omitted the row.- Final Rork remnant removed. A leftover, inert build-config entry from the removed development platform (an
expo-routeroriginpointing at the platform's domain) remained inapp.jsonafter the v1.1 cleanup. No request was ever made to it — the app contains no relative network calls that would use it — but it has now been deleted so the v1.1 "removed entirely" claim holds by construction. For clarity: identifiers such aspayd_bill_*,payd_longterm_*, andpayd_payday_checkthat appear in the app's source are names of locally scheduled notifications, not stored data, and so do not appear in Section 3's storage table. - Eight unused packages removed from the dependency list.
zod,zustand,expo-symbols,expo-image,expo-web-browser,expo-font,@expo/vector-icons, andreact-native-workletshad zero references in Payd's source and were uninstalled. None had any network behavior, so nothing about the app's data handling changes — this simply shrinks the dependency surface that Section 6's "verified against the app's full list of dependencies" claim covers.
v1.1 — July 8, 2026 Two corrections, noted here per the policy at the top of this document:
- Alpha Vantage disclosed as a third stock-quote provider. The quote fallback chain has three providers (Yahoo Finance → Finnhub → Alpha Vantage), not two. Like the others, Alpha Vantage receives only a ticker symbol, and only when you've supplied your own free API key. Sections 5.1 and 6.2 updated.
- Development-platform analytics tooling removed from the build. The app was originally scaffolded on a development platform (Rork) whose build tooling automatically injected an analytics wrapper — backed by the PostHog SDK, pointed at the platform's own relay — into every build. It was dormant in Payd (the environment variables that activate it were never set, and no data was ever transmitted), but its presence in the compiled app contradicted this document's claim that no analytics SDK is bundled. That build tooling and the SDK it carried have now been removed entirely, along with unused server-framework and permission-bearing packages (
expo-location,expo-image-picker) left over from the same template, so the claim is true by construction rather than by configuration.
v1.0 — July 8, 2026 Initial publication. Documents the local-first architecture, the three network-touching features (stock lookups, RevenueCat, simulated ad rewards), Partner Sync's unencrypted-transport limitation, AES-256 backup encryption, full-reset behavior, and platform-specific app deletion.
Questions or corrections
If you find anything in this document that doesn't match Payd's actual behavior, we want to know — please reach out so we can correct it. This document is meant to be checkable, not just readable.