Data Retention & Deletion
Owner: Ivy, Founder, Koda Digital · Effective: 2026-09-03 · Updated: 2026-11-10
1. What is stored, where, and for how long
| Data | Location | Retention | Deletion mechanism |
|---|---|---|---|
| Plaid access tokens (one per linked institution) | Firestore users/{uid}/plaid_tokens |
Until the institution is unlinked | POST /remove-item calls Plaid /item/remove (revoking the token at Plaid) then deletes the document |
| Link metadata (Item id, institution name, link date) | Firestore users/{uid}/accounts |
Until unlinked | Deleted by POST /remove-item |
| Cached Plaid responses (balances, liabilities, recurring) | Firestore users/{uid}/plaid_cache |
Served for 4–12 h; documents purged 7 days after fetch | Daily scheduled job snapshotNetWorth purges expired entries; unlinking purges that Item's entries immediately |
| Transaction ledger (date, amount, merchant, category, account/institution), including rows imported from a Mint export | The user's phone only — SQLite encrypted with SQLCipher; key in the iOS Keychain / Android Keystore (THIS_DEVICE_ONLY). On web, browser memory for the visit only. Never stored on Koda's servers — the backend passes transactions through without keeping them |
Until the user deletes the account or the app | Kept on sign-out and when an institution is unlinked (see §2); erased with its key on account deletion; removed by uninstalling. Exportable at any time from Accounts → Export transactions |
| Daily net-worth totals (assets, liabilities, net worth — no account detail) | Firestore users/{uid}/net_worth_history |
Indefinite (this is the product's history feature) | Deleted on full account deletion |
| Manual assets entered by the user | Firestore users/{uid}/manual_assets |
Until edited/removed by the user | User action / full deletion |
| Bank credentials | Never stored — entered only inside Plaid Link | — | — |
| Sign-in session | iOS Keychain / Android Keystore (expo-secure-store), excluded from device backups |
Until sign-out or remote revocation | Sign-out, "Sign out of every device", account deletion, or uninstall |
| Last net-worth summary (totals + per-account name and balance; no transactions) | iOS Keychain / Android Keystore (expo-secure-store), encrypted at rest, excluded from backups |
Replaced on every successful load; lets the app open offline with an "as of" time | Erased on sign-out and on account deletion; uninstalling removes it |
| Bills and balances screens | Not persisted on the device; fetched on demand | — | — |
| Backend logs | Google Cloud Logging | 30 days (default) | Automatic |
| Crash reports (error, stack trace, app version, device model, OS version — no account data, balances, transactions, email, screenshots or replays; IP not stored) | Sentry, only when a DSN is configured | Up to 90 days (Sentry plan default) | Automatic expiry; can be purged from the Sentry project |
Plaid's own retention of Item data is governed by Plaid's policies; revoking an Item via
/item/remove ends Koda's access.
2. Deletion procedures
- Unlink an institution:
POST /remove-item { item_id }— revokes at Plaid, deletes the token and all cached data for the Item. Exposed in the app's Accounts screen. The phone keeps that bank's transactions, so spending history survives disconnecting or re-linking it; they keep the institution name they were recorded under. Deleting the account removes them. - Full deletion (close the account) — self-service in the app: Accounts → Delete account and all data, confirmed twice. The backend (
POST /delete-account) revokes every Item at Plaid via/item/remove, deletes the entireusers/{uid}document tree — tokens, link metadata, cache, net-worth history and manual assets — and then deletes the Firebase Authentication user, which ends every session on every device. The app then erases the transaction ledger and its encryption key, its on-device session and the net-worth summary. A copy of the ledger on another of the user's devices is erased when that device's app is deleted; Koda has no way to reach it. The daily snapshot finds nothing to record once the tree is gone. A Plaid revocation that fails is reported back rather than silently skipped, so data is never left linked without the user knowing. - Deletion by request: anyone who cannot use the app can email contact-us@kodadigital.ai; the operator performs the same deletion within 7 days.
- Backups: Firestore is not exported or backed up elsewhere, so deletion is complete once the documents are removed. Crash reports in Sentry hold no account data and expire on their own schedule.
3. Minimisation
Only fields needed for the product are stored. The transaction ledger (on the phone) keeps the fields the
Activity and Trends screens display plus the identifiers needed to reconcile updates
from Plaid; of Plaid's location data only city and region are kept, never street address or
coordinates. Net-worth history keeps three totals per day, never account-level detail. Crash
reports are sent with sendDefaultPii off, no user identity attached, no session replay or
screenshots, and breadcrumbs stripped of request bodies and URLs' query strings. The version
check at launch (GET /app-config) is unauthenticated and returns only a version string; it
receives no user data.