Draft. These details are incomplete and not yet legally binding. Bagful is in a non-public beta.
Privacy policy
Courtesy translation. The German version is the legally authoritative one.
1. Controller
«Anbietername eintragen»
«Straße und Hausnummer»
«PLZ» «Ort»
Deutschland
Email: «kontakt@bagful.app»
No data protection officer has been appointed; the threshold in §38 BDSG (generally at least 20 people permanently engaged in automated processing) is not met.
2. What we process
| Data | Purpose | Legal basis |
|---|---|---|
| Name, email address, password (stored only as a hash) | Creating and running your account | Art. 6(1)(b) GDPR |
| Your device's push token, its platform, and when it was last seen | Delivering notifications to that device. The token belongs to the app installation rather than to you: signing into a different account on the same device moves it to that account. It is removed when you sign out and when you delete your account. The data export omits the token itself — it is a key, not a fact about you | Art. 6(1)(b) GDPR |
| Profile picture, if you upload one | Making you recognisable in your bags. The picture is served publicly from a random, unguessable address and is shown in the app only to people you share a bag with. Anyone who knows the address can fetch it; replacing or removing the picture retires that address. The public invitation preview never shows pictures, only initials | Art. 6(1)(b) GDPR |
| Guest account: a random identifier, no name, no email address | Using Bagful without registering, in particular when joining via an invitation link | Art. 6(1)(b) GDPR |
| Session identifier, session IP address and user agent | Keeping you signed in, detecting abuse | Art. 6(1)(b) and (f) GDPR |
| Lists, items, quantities, units, notes, categories, emojis | The service itself | Art. 6(1)(b) GDPR |
| Memberships and roles, invitation tokens and bag codes, expiry and use counters | Sharing bags with other people | Art. 6(1)(b) GDPR |
| Who added or checked off an item, and when | Making collaboration legible — visible to every member of that list | Art. 6(1)(b) GDPR |
| Who has taken on fetching an item, and when | Avoiding duplicate shopping — visible to every member of that list, and readable by a connected AI assistant if you have one. The entry is cleared when the item is checked off or removed, when you leave the list, and when your account is deleted. | Art. 6(1)(b) GDPR |
| Notifications: what happened, who did it and in which bag — stored as a snapshot taken at the time of the event | Telling you about changes in your bags. A notification about you stays in other members' inboxes even if you delete your account — your name is removed, the event itself remains. | Art. 6(1)(b) GDPR |
| Units you create: a short form and a name, both your own words | Your own measures, like "bunch" — visible to every member of a bag where you have used one on an item. They are not translated and stay exactly as you write them. | Art. 6(1)(b) GDPR |
| Registered AI clients, consents granted, access tokens | Connecting your own AI assistant over MCP | Art. 6(1)(a) GDPR |
| Crash reports and performance measurements of the application: error message and stack trace, device model and technical device state, the version of the app and of the operating system, launch and load times, the route visited and the last steps taken before an error — not linked to your account | Finding and fixing faults, keeping the app fast — our legitimate interest in a service that works. Reports carry no account identifier, only a random identifier of the app installation | Art. 6(1)(f) GDPR |
We process no special categories of personal data within the meaning of Art. 9 GDPR. The service is not directed at children.
3. Recipients and processors
Data processing agreements under Art. 28 GDPR are in place with all of the following.
| Provider | Role | Processing location |
|---|---|---|
| Neon | Database — all account and list data | AWS eu-central-1, Frankfurt |
| Cloudflare | Running the application, caching, realtime connections, serving the website, bot protection (Turnstile), and sending verification and password-reset email. Processes the IP address of every request | EU data centres, global network |
| Sentry | Error diagnostics in the web application, the api / realtime workers and the mobile app | EU region (Frankfurt) |
| Expo (650 Industries, Inc.) | Performance diagnostics of the mobile app (EAS Observe), and relaying push notifications to Apple's and Google's push services (Expo Push) | USA — transferred under the EU-U.S. Data Privacy Framework, for which Expo is certified, and the EU Commission's standard contractual clauses |
On Sentry specifically: the error message, stack trace, page or route visited and browser/OS or Worker runtime version are transmitted. The IP address is technically present on receipt but is not stored (sendDefaultPii: false). There is no session replay and no performance tracing. Invitation tokens and connection tickets are stripped from URLs before transmission.
In the mobile app, Sentry is wired in under the same commitments. The app reports crashes and unhandled errors; what is transmitted is the error message and stack trace, the device model, the version of the app and of the operating system, technical facts about the device at the moment of the error (memory, battery level, orientation, time zone and language, for instance) and the last steps before it: the addresses of our API that were called, without tokens, the app's console output, and the controls last touched, with their labels — for an item row, that includes the item's name. So that we can tell how many installations an error affects, Sentry also counts the start and end of each app session under a random identifier the app generates for this installation — it is not an account, cannot be matched to your account, and is issued afresh on a reinstall. No screenshots, no view hierarchy, no session replay, no performance tracing, no profiling; your account is never named to Sentry. Invitation tokens and connection tickets are stripped before transmission here too, and the IP address is not stored, as above.
On Expo (EAS Observe): the mobile app measures how long it takes to launch and how long each screen takes to appear and to become usable, and transmits those timings to Expo. Each transmission carries the route name — the identifiers of bags, messages and invitations are removed from it first — the device model, the operating system and its version, the version and build of the app, your language setting, and a random identifier of the app installation that is not an account and is issued afresh on a reinstall. The launch timing adds technical facts that explain a slow start: battery level and low-power mode, thermal state, network type (Wi-Fi or cellular) and a summary of the network requests made during launch (count, data volume, duration, slowest server). With these measurements, the contents of your bags, your name and your email address do not leave the device. This applies to the builds we distribute through TestFlight, Google Play or as previews. The development client sends nothing: not to Expo, because development builds do not dispatch measurements, and not to Sentry, because we give it no access there.
Push notifications reach your device through Expo's push service: our server hands it your device's push token, the text of the notification — that is, who did what in which bag, as it appears on your lock screen — and the bag's identifier. Expo passes this on to Apple's or Google's push service and, by its own account, does not store it.
On voice input in the mobile app: dictation is optional and stays off until you tap the microphone in the app. When you do, the recording is handed to your own device's speech recognition (iOS Speech, or Android SpeechRecognizer). On many devices that recognition does not happen on the phone but on Apple's or Google's servers, under those companies' own terms and within your own relationship with your operating system's vendor. We have no control over it and cannot enter into a processing agreement about it — which is exactly why Apple and Google are named here and not in the table above. Bagful itself never receives or stores the recording. All that reaches us is the text that comes back, and even that only once you actually save the item — cancel the dictation and nothing is left behind. On iOS the permission prompt tells you the same thing before speech recognition is used for the first time.
4. Disclosure to AI assistants on your instruction
Bagful exposes an MCP interface that lets you connect your own AI assistant (such as Claude or ChatGPT) to your bags. That connection is created solely by your explicit consent on the consent screen. The connected service receives the bags:read scope (read bags and items) and, where granted, bags:write (create, change, check off and delete items).
The people you share a bag with see that you have connected an assistant, and which of these two scopes it holds: the app draws it under your name in that bag's list of people. They do not see which assistant it is. Nobody outside the bag sees it — the public invite preview never shows it — and it disappears when you withdraw your consent (Art. 6(1)(b) GDPR).
The provider of the assistant you choose is an independent controller for the processing that happens on their side; this is not processing on our behalf.
You may withdraw your consent at any time with effect for the future (Art. 7(3) GDPR): in the app under Account, in the connected-assistants section, or by an email to the address above. Withdrawal ends the assistant's access immediately. It does not affect the lawfulness of the processing carried out until then, and it does not delete the data the assistant has already received — for that, contact the assistant's provider, who is independently responsible for it. Your own data in Bagful, including the local copy on your device (section 7), is unaffected by the withdrawal.
5. Fetching recipe pages
When your AI assistant asks for the contents of a recipe URL you named, our server fetches that page, not your browser. The site in question therefore sees our server's IP address, not yours. Only publicly reachable addresses are fetched. The URL and the extracted text are cached for one hour.
6. Realtime synchronisation
An open list holds a WebSocket connection so changes appear immediately for every member. The connection is authorised with a short-lived signed ticket. Change events travel over it; nothing is stored there.
7. Cookies and local storage
| Purpose | Lifetime |
|---|---|
| Session cookie keeping you signed in (HttpOnly, SameSite=Lax) | Until the session expires or you sign out |
| Short-lived cache of the session check | 5 minutes |
| Bot protection (Turnstile) on sign-up and guest access | Only for the duration of the check |
| Mobile app: session token in the device's secure storage | Until you sign out |
| Mobile app: local shopping-list copy and pending sync queue (SQLite on the device) | Until you sign out or delete the account; wiped with the session |
All of the above are strictly necessary for the service you explicitly requested, within the meaning of §25(2) no. 2 TDDDG. No consent is required and we run no consent banner. We use no analytics, tracking or advertising cookies. The web app does not keep a local shopping-list database — only the mobile app does.
8. Retention
- Account and list data: until the account is deleted.
- Sessions: until they expire or you sign out.
- Notifications: 90 days, then deleted — the same window as deleted items, because a notification should not outlive the thing it describes.
- Units you create: until you delete them or your account is deleted. A unit you have already used on an item stays there as text — including in bags you have left, and including after your account is deleted. Deleting the unit removes it from items in bags you belong to; elsewhere the text remains.
- Invitation links: until their expiry date or their permitted number of uses is reached. Bag codes have neither an expiry nor a use limit — they are kept until the bag's owner replaces or turns off the code, until the bag is deleted, or until the account that created it is deleted.
- Cache of fetched recipe pages: one hour.
- Sent emails: Cloudflare retains the message and its content in an activity log for seven days.
- Crash reports and performance measurements: Sentry deletes error events after at most 90 days (the provider's default). Expo keeps measurements for at least 60 days; Expo names no upper bound, and we cannot set one there.
- After deletion, data may remain in our database's change history for the length of its recovery window — currently seven days — after which it is permanently overwritten.
9. Account deletion
When an account is deleted, every list owned solely by that account with no other members is permanently deleted along with all its items. Lists that do have other members are automatically transferred to the longest-standing remaining member, so that the other members' data is preserved. Membership is removed from all other bags, and remaining entries are anonymised so they can no longer be attributed to the deleted account. Invitation links and bag codes created by that account stop working — including the code of a bag that was transferred to another member. The new owner can create a fresh code at any time.
10. Your rights
You have the right of access (Art. 15), rectification (Art. 16), erasure (Art. 17), restriction of processing (Art. 18), data portability (Art. 20) and objection to processing based on legitimate interests (Art. 21 GDPR). Consent you have given may be withdrawn at any time with effect for the future (Art. 7(3) GDPR).
The main ones you can exercise directly in the app, under Account:
- Portability (Art. 20): "Download my data" gives you all your bags, items and invitations as a JSON file.
- Erasure (Art. 17): "Delete my account" removes your account and data under the rules in section 9. Before you confirm, you are shown which bags will be deleted and which will be transferred.
- Withdrawing consent (Art. 7(3)): in the connected-assistants section you can cut off any assistant's access at any time (section 4).
For anything else, send a message to «kontakt@bagful.app».
You also have the right to lodge a complaint with a data protection supervisory authority (Art. 77 GDPR). The authority competent for us is: «zuständige Landesdatenschutzbehörde» («https://www.bfdi.bund.de/DE/Service/Anschriften/anschriften_node.html»).
11. No automated decision-making
There is no automated decision-making, including profiling, within the meaning of Art. 22 GDPR. We run no advertising and sell no data.
12. Changes to this policy
We update this policy as the service changes. The version published here, with the date shown below, is the applicable one.
Last updated: 2026-07-28