Finance · finance

Finance Privacy Policy

The short version

Finance is an app for tracking personal and family finances. We keep your data on our own server so that it syncs between your devices and between the members of a shared space.

The most important things on one screen:

What follows is the same thing, in detail. Every statement on this page has been checked against the source code of the app and the server.

Who is responsible for your data

The controller (under the GDPR) and the owner of personal data (under Ukraine's personal data protection law) is Serhii Pashchenko, a natural person, Poland, postal address: 31-356, Poland.

To get in touch about any data question: support@serhiip.dev. This is the only address at which we accept requests; we have no others.

This policy applies to:
— the Finance mobile app for iPhone (in the App Store — Finance);
— the mobile app for Android (Finance, identifier com.serhiipashchenko.capital);
— the server that runs them, including the addresses https://finance.serhiip.dev and the legacy address https://91-99-155-40.sslip.io, which remains hard-coded in previously released builds of the app.

Effective date of this version: 16 September 2026.

What data we receive and store

Account
— Email address. It is your login and the only way we can identify you. We don't confirm it by email (we don't send any emails from the app at all).
— The name you entered in your profile. It signs your records in a shared space.
— Password — only as an irreversible hash (argon2). We neither store nor see the password itself.
— Profile photo, if you uploaded one (up to 6 MB, png/jpeg/webp/heic/heif). It sits directly in our database.
— Appearance settings (theme, colour scheme, tab accents, app icon) and interface language — so that they match on all your devices.

Financial records — the core of the app
— Accounts: name, type, currency, current balance, the "savings" flag, archived/favourite status.
— Transactions: amount, currency, rate against the base currency, date, type (expense, income, transfer), account, category, your free-form note, a split across several categories.
— Categories, category groups and savings goals (how much, by what date).
— Budgets and the monthly plan: how much is assigned and how much is spent in each category.
— Subscriptions you track: name, amount, period, next charge date.

Receipts
— What we save from a receipt: the shop, the date and time of purchase, the currency, the total, the fiscal number, the original contents of the QR code and the line-by-line list of items — the name, quantity and price of each item.
— What we do NOT save: the photo of the receipt itself. It lives only in the server's memory for the duration of the request and is not written down anywhere.
— We also remember rules of the form "this item → this category", so we don't ask you twice. These rules are shared across the whole space.

Bank connections
— The personal Monobank token you pasted in, or the request identifier at a European aggregator. These secrets are encrypted in the database with AES-256-GCM.
— What comes from the bank and is saved: the bank's account numbers/identifiers, balances, currency, card type and transactions — amount, date, currency and payment description. A bank transaction description often contains the name of a shop or counterparty; with Polish banks, the account name may include the account holder's name and the last 4 digits of the IBAN.
— What comes but is NOT saved: the bank customer's name, the full IBAN, the masked card number, the transaction's MCC code. Our code simply doesn't read these fields.

Sessions
— For every sign-in we keep a session row: your identifier, an irreversible hash of the refresh token (the token itself is not in the database), the expiry date and a revocation flag.

Technical
— We do NOT store your IP address. It is used only in the server's memory as a rate-limiting counter (protection against password guessing) and never reaches the database. Our database does have columns for IP and device type, but the code writes nothing into them — they are always empty.
— We don't keep an HTTP request log. The server logs contain only application startup, cache errors, background-job warnings and the error text from the receipt recognition service.

What we don't collect at all

Verified by searching the entire code of both apps and the server:

One honest caveat: this is the result of a line-by-line review of the source code and the dependency lists, not a formal audit of the compiled binary.

Why we need this data and on what legal basis

A contract with you (Art. 6(1)(b) GDPR) — without this data the app simply doesn't work:
— email and password hash: so that you can sign in;
— accounts, transactions, categories, budgets, subscriptions: the bookkeeping itself, the reports and the plan;
— space membership: to decide whose data to show to whom;
— sessions: so as not to make you type your password every 15 minutes.

Your consent (Art. 6(1)(a)) — you switch this on yourself, and can switch it off at any time:
— the camera and receipt photos, including sending the image to an external recognition service;
— connecting a bank and regularly reading the statement;
— connecting to the Polish state hub for electronic receipts;
— the profile photo;
— inviting another person into a shared space.

Our legitimate interest (Art. 6(1)(f)):
— rate limiting and protecting sign-in against password guessing;
— minimal technical logs, so that we can fix breakages;
— storing session hashes, so as to spot a stolen token and break the session chain.

The law (Art. 6(1)(c)) — when we are obliged to answer your rights request or a lawful request from an authorised body.

A separate word about sensitive data. The category of an expense can say more about you indirectly than the amount does: a purchase at a pharmacy or a payment to a clinic is about health, a donation is about beliefs. We don't collect such data deliberately and don't draw any conclusions about you from it, but it inevitably ends up in the app together with receipts and transactions. If this matters to you — don't connect receipt recognition, and enter such expenses manually under a general category.

Automated decisions and profiling

We don't take automated decisions about you that would have legal or similarly significant effects for you. There is no scoring, no creditworthiness assessment and no "the algorithm decided" refusals in the app.

We have exactly one piece of automation: suggesting a category for an item from a receipt. It works from a dictionary, from your past decisions and — when the corresponding key is enabled on the server — from a language model's suggestion. It is always only a suggestion: you change any category by hand with a single tap, and the app will remember your choice.

Who sees your data

You. In a personal space, only you see the data (and we, as the owner of the server — technically it is accessible to us, and we don't pretend otherwise).

Members of a shared (family) space. This is the most important thing to understand before you send an invitation.

When a person accepts an invitation, they see:
— all transactions of the space — amounts, dates, currencies, accounts, categories and your free-form notes, including the payment descriptions imported from your bank with the names of shops and counterparties;
— all accounts and their current balances;
— all budgets, the plan and subscriptions;
— all receipts with the line-by-line contents of the purchases: in which shop, when, for how much and exactly which items;
— the summary reports "what we buy most often" and "what is about to run out", compiled from the purchases of all members;
— who exactly entered each record: the author's name and profile photo are shown next to the transaction;
— the email address of every member of the space.

There is no "this expense is visible only to me" mode in the app. Separation by author inside a shared space does not exist on any screen. If there are expenses you don't want to show — keep them in your personal space.

The profile photo — over a direct link, without sign-in. Your avatar is served at an address of the form /api/users/<your identifier>/avatar with no password and no authorisation: that is how the apps themselves load it. This means that anyone who has this link will see the photo without logging in to Finance. The identifier is random and cannot be guessed, but it is legitimately visible to all members of your shared spaces. The response is marked as cacheable for a year, so even after you delete the photo, copies already downloaded may remain for some time in the caches of browsers and intermediate servers. If this is unacceptable to you — don't upload a photo; your initials are shown instead.

An invitation link discloses part of the data before sign-in. The invitation preview page opens without authorisation and shows: the name of the space, the name of the person who invited you and — if the invitation is addressed to a specific person — the email address of the person being invited. Forward the invitation code only to the person it is meant for.

Us. Serhii Pashchenko has access to the database — technically, in order to run the server. We don't read your transactions out of curiosity and don't use them for anything other than operating the service.

Bank connections: we read, but we don't pay

Finance can connect to a bank so that you don't have to enter transactions by hand.

What we can do: read the list of your accounts, their balances and the statement — the amount, date, currency and description of each transaction.

What we CANNOT do: make a transfer, pay for anything, debit money, change limits or do anything else with your money. For the Monobank token we use, only the right to read the statement and customer data has been requested, and the European connection works through a licensed aggregator in "account information only" mode. There is not a single payment-creating request in our code. Even if we wanted to — the app has no such function.

How the access is stored: the bank secret (your personal Monobank token or the request identifier at the aggregator) is encrypted in the database with AES-256-GCM, using a key kept separately from the database.

How often we contact the bank: a background sync job contacts the bank regularly for as long as the connection is active. Disconnect the bank in the app and the requests stop.

Important: disconnecting the bank does NOT delete the accounts and transactions already imported. This is deliberate, so that you don't lose your history. If you want to get rid of the data as well — delete the account or the transactions manually; deleting an account also deletes all of its transactions.

There is one more way transactions can get into the app: Monobank can send us notifications about new transactions on a connected account. This is read-only as well — money cannot be moved by such a notification.

Receipts: what happens to the photo

There are two ways to enter a receipt.

1. By QR code. The app reads the QR and asks the fiscal operator that issued the receipt for its contents. In this case only the receipt identifier from the QR code goes out (for receipts from the online office of the tax service of Ukraine — the entire address from the QR as it is written there). No photo is sent anywhere.

2. By photo. If there is no fiscal QR, the photo of the receipt goes from our server to an external recognition service — currently the Google Gemini model. It is given the whole image and the instruction text "extract the shop, the date, the total and the list of items". Neither your name, nor your email, nor your account identifier is added to this request — but the receipt itself usually shows the name and address of the shop, the date, the time and the full list of purchases. This is probably the most sensitive data transfer in the whole app: a pharmacy receipt reveals far more than the line "Pharmacy, UAH 240".

What happens to the photo on our side: it is not saved. The file lives only in the server's memory for the duration of the request; there is not a single field for a receipt image in the database, it is not written to disk and it does not end up in the logs. Only the recognised text goes into the database: shop, date, total, list of items.

What happens to the photo at the recognition service: that is governed by the service's own terms, not by our code. We cannot guarantee that it does not use the images it receives according to its own rules.

If this doesn't suit you — don't use photo recognition. Scanning a fiscal QR and manual entry work without any transfer of images.

Separately: if the additional auto-categorisation key is enabled on the server, the names of unrecognised items and the list of your category names are sent to Anthropic (USA). There is no name, email or account identifier in that request. In our current server configuration this path is switched off.

Polish electronic receipts. If you have connected the state hub for electronic receipts of the Ministry of Finance of Poland, we regularly ask it for your receipts, passing the identifier issued to you (KID). The private part of the KID is stored encrypted on our side and is not given out to anyone except the hub itself; the public part is the one you show the cashier as a barcode.

Who we pass data to

The complete list — there are no other recipients in the code.

We don't hire contractors for marketing, don't pass data to ad networks and don't sell it to anyone.

Transfers of data outside the European Union

The server and the database are located in the European Union (Germany).

Data leaves the EU in three cases:
— a receipt photo goes to Google for recognition (USA and global infrastructure);
— item names may go to Anthropic (USA), if fallback auto-categorisation is enabled;
— if you have connected Monobank, your bank's data is processed in Ukraine.

The legal basis for such transfers: the EU Standard Contractual Clauses (SCC) in our agreements with the recipients and Google LLC's participation in the EU–U.S. Data Privacy Framework; for the transfer to Ukraine — your explicit consent, given by connecting a Ukrainian bank (Article 49(1)(a) GDPR). We state the exact mechanism for each recipient here and update it when a provider changes.

If you don't want your data to leave the EU, don't use photo-based receipt recognition and don't connect the Ukrainian bank — the rest of the app runs entirely on the server in the EU.

How long we keep data

The general rule: as long as you have an account, your data lives. We don't delete it on a schedule, because the point of the app is a history spanning years.

In detail:
— Account (email, name, password hash, settings, profile photo) — until the account is deleted.
— Accounts, transactions, categories, budgets, subscriptions — indefinitely, as long as the space exists, or until you delete them yourself.
— Receipts and their line items, including the stored contents of the QR code — indefinitely; deleted manually or together with the space.
— Item auto-categorisation rules — together with the space or the category.
— Bank connection — until you press "Disconnect bank".
— Connection to the Polish receipts hub — until it is disconnected or until the space is deleted.
— Session records — honestly: they are not deleted. On sign-out and on token refresh we only mark the record as revoked. The rows physically disappear only together with the account. We are fixing this (see the section "What we know about and are fixing").
— Invitations — also not deleted: the invitation row, together with the invitee's address, stays in the database after the invitation has been accepted or has expired. It is deleted only together with the space or with the inviter's account. Also on the list to be fixed.
— Exchange rates — indefinitely; they contain no personal data.
— The cache on the server (membership, the computed plan and the summaries) — from 1 to 5 minutes, exchange rates — 30 days; it expires by itself.
— Server logs — until the container is recreated at the next update; there is currently no time limit. The logs contain no email addresses, passwords, tokens or amounts.

What happens when an account is deleted is described in detail on a separate page, "Account deletion". It also honestly lists what survives deletion.

What is stored on your phone

The app keeps a copy of your data on the device so as to work without internet and open instantly. The copy is the transactions of the last 6 months (up to 10,000 records per space), accounts, categories, budgets, spaces and exchange rates.

On iPhone
— The local database is in the app's sandbox. It is not encrypted with a separate app password; it is protected by the iPhone's own encryption.
— Important: by default this database is synced to your personal iCloud (the private iCloud database, accessible only under your Apple ID). That means a copy of your transactions, balances and notes also sits with Apple, under Apple's rules. We have no access to that copy. If you don't want this — turn off iCloud for Finance in your iPhone's system settings.
— Sign-in tokens are kept in the system keychain (Keychain). They are marked as accessible after the device's first unlock, which means they end up in the encrypted iPhone backup and can be restored onto another phone of yours.
— A profile snapshot (email, name, photo link, appearance settings) is kept in the app's ordinary settings in plain form.
— Honestly: on sign-out the app wipes transactions, budgets, categories, accounts and spaces, but does NOT wipe your email address and the exchange rate cache. This is on the list to be fixed.

On Android
— The local database is an ordinary SQLite file in the app's sandbox, not encrypted with a separate app key; it is protected by device encryption.
— Sign-in tokens are kept in encrypted storage (EncryptedSharedPreferences, with the key in Android's hardware-backed store).
— Backup to Google Drive is disabled for the app — the database and settings don't go to Google's cloud.
— If you have enabled the app lock with a PIN code, what we store is not the PIN itself but a digest of it. Honestly: this digest is not a cryptographic hash, and our PIN protection is weak. It is kept in encrypted storage, but you shouldn't rely on it as a serious barrier. We are reworking this.
— Receipt photos taken with the camera currently stay in the app's temporary cache and are not deleted by our code (they disappear when the app's cache is cleared). We are fixing this too.
— On sign-out, transactions, budgets, accounts, categories and spaces are wiped, but a record with your email address remains. We are fixing this.

Biometrics. The app neither receives nor stores your fingerprint or Face ID: the operating system answers us only "yes" or "no".

How we protect data

What has been done:
— Communication between the app and the server goes over HTTPS with an automatically renewed certificate.
— Passwords are stored only as an irreversible argon2 hash. We can't see them and can't recover them.
— Session refresh tokens are stored only as a hash. The token changes on every use; if someone tries to use an old stolen token, the whole session chain is broken and you are signed out on all devices. Changing the password revokes all sessions.
— Bank secrets and the private access key for the Polish receipts hub are encrypted in the database (AES-256-GCM).
— The database and the cache are not exposed to the internet — they cannot be reached from outside the server.
— The technical API documentation is switched off in the production system.
— Passwords, email addresses, tokens and amounts are not written to the logs. No HTTP request log is kept.
— Every request checks whether you belong to the space whose data you are requesting.

What we do NOT do and will not claim:
— We don't encrypt the database with "end-to-end" encryption. Financial records sit in the database in readable form — otherwise the server couldn't calculate your reports. Only the bank secrets are separately encrypted.
— We cannot guarantee that data passed to the receipt recognition service is processed according to our rules — its rules apply there.

If there is a breach. If a security breach occurs that could harm you, we will notify the supervisory authority within 72 hours, as the GDPR requires, and write to you if the risk to you is high.

What we already know about and are fixing

We decided not to pretend that everything is perfect. Here are the weak spots we found while reviewing our own code, and what is happening with them:

We keep this list up to date: when an item is fixed, it disappears from here.

Your rights

Under the GDPR (and under Ukraine's personal data protection law, if you are in Ukraine) you have the rights to:

How to exercise them: write to support@serhiip.dev from the address the account is registered to. We answer free of charge and no later than within a month. If the request is complex, we may extend the deadline by a further two months — and we will certainly warn you about that within the first month. If we are not sure the message is from you, we will ask you to confirm your identity — for example, to reply from the account's address or to name details known to the owner.

Where to complain

If you believe we are handling your data wrongly, write to us first — almost everything gets resolved by email. But you are entitled to go to a supervisory authority without doing so:

You may go to the authority in your place of residence, at your place of work or at the place of the alleged infringement.

Children

Finance is not intended for children. An account can be created from the age of 16; in Poland and a number of EU countries, giving consent on your own to the processing of data in online services is possible from the age of 16.

We don't verify age at registration — we don't collect a date of birth at all. If you are a parent or guardian and have found out that a child has created an account without your consent, write to support@serhiip.dev: we will delete the account and the data associated with it.

If you run a family budget and want to show a child the expenses — do it from your own account, without creating a separate one for them.

Changes to this policy

If we change the way we work with data, we will update this page and change the effective date at the top.

If a change is significant — for example, a new data recipient or a new purpose of processing appears — we will announce it in the app before the change takes effect.

We keep previous versions and can send them on request to support@serhiip.dev.

This page is available in Ukrainian, Russian, Polish and English — the same languages as the app itself.