Hazely Privacy Policy

Effective 14 August 2026 | Version 3.2

About this version. Version 3.0 replaces version 2.1 in full. Version 2.1 described a server architecture Hazely no longer runs and, in several places, described data handling that did not match the app. Every statement in this version was checked against the shipping code and the deployed backend on 4 August 2026. Where the app does something we would rather it did not, this policy says so.

Language. This policy is published in English at hazely.nl/privacy and in Dutch at hazely.nl/privacybeleid, at the same version number and the same effective date. Neither language version is designated as prevailing over the other: they are maintained together and are meant to say the same thing. If you find a difference between them, tell us at privacy@hazely.nl — we will correct it and republish both. The app itself is available in English and Dutch.

1. Who we are (controller)

For the purposes of the EU General Data Protection Regulation 2016/679 (GDPR) and the Dutch Uitvoeringswet AVG (UAVG), the controller of the personal data described in this policy is:

Ali Khoshraftarmonfared, a natural person established in the Netherlands, trading as Tesseractive — the developer name published on the Google Play listing for nl.hazely.app.

Hazely is not run by a company. There is no BV, no other legal entity and no Chamber of Commerce (KvK) registration behind it yet, so this section gives you no KvK number, no VAT number and no company legal form: there are none to give, and we are not going to invent one. A Dutch business registration is being arranged. When it exists, this section will be updated and this policy reissued under a new version number.

Postal address. We do not publish one, because the only address that exists today is a private home address. You can still have it: ask at privacy@hazely.nl and we will send it to you in writing. That is also the address to use if you need to serve something formally.

We are established in the Netherlands, so no representative under Art. 27 GDPR is required.

Contact channelAddress
Privacy and data-protection questionsprivacy@hazely.nl
Data subject rights requestsprivacy@hazely.nl — see hazely.nl/data-request for what to include
Data-protection contactdpo@hazely.nl — see the note below
Legallegal@hazely.nl
Security incidents and vulnerability reportssecurity@hazely.nl
Illegal content and abuse reportsabuse@hazely.nl
Postal addresson request, from privacy@hazely.nl

About dpo@hazely.nl. We have not designated a Data Protection Officer, and we are not required to under Art. 37(1) GDPR: we are not a public authority, and Hazely does not operate at the scale that provision contemplates. dpo@hazely.nl reaches the same person as privacy@hazely.nl. It is published because earlier versions of this policy published it and it still appears in the app and on our website. Treat it as a routing alias for privacy correspondence, not as the designation of a data protection officer under Arts. 37–39 GDPR. If that ever changes, we will say so here and notify the Autoriteit Persoonsgegevens as Art. 37(7) requires.

2. Scope and definitions

This policy covers the personal data we process when you install, open, or use the Hazely mobile app — nl.hazely.app on Android and nl.hazely.ios on iOS — and when you visit hazely.nl.

Hazely is an information-only app. It shows publicly listed licensed coffeeshops, their opening hours, ratings and house rules, gives walking directions, extracts text from a photo of a printed menu, and shows a reference catalogue of cannabis strains. It sells nothing, takes no orders, processes no payments, and has no user accounts.

This policy does not cover coffeeshops, third-party websites, or any other service you reach from a link in the App. Read their own privacy notices.

Two companion documents form part of this notice:

This policy is also the privacy policy referenced in Hazely's Google Play Data safety section and in the App Store privacy label. Those two disclosures must say the same things this policy says. If you spot a difference, tell us at privacy@hazely.nl.

3. What data we process

3.1 Data you give us directly

DataWhenWhat we do with itWhere it goes
Age confirmation (a yes/no flag plus a timestamp)First launchRecords that you confirmed you are 21 or olderYour device only. Android: app preferences, excluded from Google Backup. iOS: UserDefaults, which is included in your iCloud backup by default
Acceptance of the Terms, this policy and the Cookie PolicyFirst launch, and after a change to the documents that gate the AppEvidence that you were asked and answeredYour device only. Android stores the exact version string you accepted; iOS currently stores only that you accepted, not which version
Your consent choices (analytics, crash reporting, personalised ads)First launch and whenever you change them in SettingsControls what optional processing runsYour device only
Text you type into the map search barAs you searchFilters the visible shop list on your device. The text itself never leaves your device. If you have Analytics on, we send how many characters you typed and how many results came back — not what you typedYour device. Only the length and the result count reach Google, and only if Analytics is on
Text you type into the strain catalogue searchWhen you search for a strain we do not already haveThis one is sent. It goes to our server, and on to Google's Gemini API, so an entry can be generated. If Analytics is on, we separately record only the length of what you typed, not the textOur server (Google Cloud, Belgium); Google Gemini; and the text appears in our server request logs
Photographs of coffeeshop menu boardsWhen you tap "Scan Menu" and confirmAI text extraction — see §3.3Our server, then Google Gemini. The image itself is never stored
Owner verification / correction requests (shop name, contact name, business email, optional phone, message)When you submit the owner formChecking and correcting public listing informationStored in our database (Firestore, Belgium)
Illegal-content and abuse reports (message, and an email address if you choose to give one)When you submit a reportHandling the reportStored in our database (Firestore, Belgium)
Favourites (the IDs of shops you starred)When you tap the starShows your favouritesYour device only — the list is never transmitted. But if Analytics is on, the act of favouriting sends a shop_favorited event with the shop ID to Google

We do not operate a feedback form, and there is no crash-reporter comment box. Version 2.1 said we did. That was wrong and has been removed.

3.2 Data collected automatically

Most of the rows below are off unless you switch them on. Firebase Analytics, Crashlytics and Performance Monitoring all ship disabled in the app manifest and stay disabled until you consent. A few rows run regardless of your choices; those are marked.

DataPurposeWho receives itRuns when
Foreground device location (precise or approximate, whichever you granted)Centre the map, draw your location dot, measure walking distanceNot sent to Hazely's servers. Used on your deviceOnly while the App is open and only if you grant the OS permission. We do not ask for background location
Walking route request — your starting coordinates and the destination coordinatesProduce the walking route you asked forMapbox Inc. (United States). The coordinates travel in the request URLOnly when you tap "Navigate"
Mapbox Maps SDK telemetry — device location and map-usage data collected by Mapbox's own SDKMapbox's own map-improvement purposesMapbox Inc.Android: only if you turned Analytics on. The App switches Mapbox telemetry off otherwise, and does so again from the stored setting each time the map is built. iOS: the App does not switch it off — use Mapbox's own opt-out in the map's attribution control. See §10
Mapbox monthly-active-user "turnstile" eventMapbox's licence meteringMapbox Inc.Whenever the map is on screen, even with Mapbox telemetry switched off. The SDK offers no way to suppress this one
Map tile and style requestsDraw the mapMapbox Inc. (your IP address and the area you are looking at)Whenever the map is on screen
On-device proximity checkEvery time your location updates, the App measures the distance to the nearest coffeeshop. Within 50 metres it holds that shop's name in memory so it can show the "you arrived" card and suggest nearby foodNobody. This never leaves your deviceWhenever location is granted and the App is open
Crash reports — stack trace, crash dump, device model, OS version, app version, and a per-installation identifier generated by GoogleFixing crashesGoogle (Firebase Crashlytics)Only if you turn crash reporting on
Selected-shop crash keys (Android) — the ID and name of the shop you last opened are attached to any crash reportReproducing crashesGoogle (Firebase Crashlytics)Only if you turn crash reporting on
Crash titles opened as tracker issues — crash title, subtitle, app ID and app versionOur own bug trackingGitHub, Inc. (United States)When Crashlytics raises a new-crash or regression alert. No crash means no issue
App-usage events — see the full list belowUnderstanding which features are usedGoogle (Google Analytics for Firebase)Only if you turn Analytics on
Advertising ID (Android)Collected by the Analytics SDK by Google's defaultGoogleOnly if you turn Analytics on. Note: this rides the Analytics switch, not the personalised-ads switch. There is currently no separate control for it. From Android 1.0.7 onward, collection of the Advertising ID is switched off entirely
Performance traces — app start time, screen rendering, and automatic traces of the App's own network requests (host and path, payload sizes, response codes)Finding slow screens and slow requestsGoogle (Firebase Performance Monitoring)Controlled by the Analytics switch. There is no separate control
Remote configuration fetch — a Firebase installation identifier, app version, platform, language and countryFeature flags, and the "update required" kill switchGoogle (Firebase Remote Config)On both platforms, only after you have answered the consent screen — whatever you answered. Nothing is fetched before that
App and device attestation token (Android) — Google Play Integrity via Firebase App CheckStopping abuse of our API by fake clientsGoogleOn API requests once you have answered the consent screen — including if you rejected everything optional
Play in-app update check (Android)Telling you when a new version is requiredGoogle PlayAt launch and on resume, before the age gate
IP addressDelivering the response; rate limiting; abuse preventionGoogle (Cloud Functions, Cloud Logging, Firebase Hosting)Every request to our server. Your full IP address is received and logged. It is not truncated. Version 2.1 claimed truncation to /24 and /48; that described a server we no longer run
Client headers X-Hazely-Client, X-Hazely-App-IdTelling Android and iOS traffic apartGoogle (server logs)Every API request. These identify the platform, not you
Store review prompt countersDeciding when to show the "rate this app" promptNobody — device only. The prompt itself is shown by Google Play / AppleDevice only
Advertising—Nobody todayThe AdMob SDK is built into the Android app, but every ad placement is switched off and no ad is requested. See §4.3

The exact analytics events. If — and only if — you turn Analytics on, the Android app can send these events to Google. Each is tied to a Firebase app-instance identifier: a pseudonymous per-installation ID that Google can link across your sessions.

EventWhat it carries
screen_viewedwhich screen
age_gate_confirmednothing
consent_recordedyour three consent choices
settings_opened, owner_portal_openednothing
shop_detail_openedshop ID and shop name
navigate_tappedshop ID and shop name
menu_scan_startedshop ID and shop name (sent when you tap Scan, before the disclosure and the camera)
search_submittedthe number of characters you typed and the number of results — never the text
nearby_food_tappedshop name
review_openedshop ID and shop name
shop_favoritedshop ID
strain_searchsource, whether a match was found, and the length of your query — not the query itself
strain_detail_openedstrain ID and type
ad_placement_viewedwhich ad slot. Not sent today, because no ad slot is enabled

The iOS app sends a smaller set: search_submitted (query length and result count), shop_selected (shop ID and name), strain_viewed (strain ID and strain name — the Android equivalent sends the type instead of the name), menu_scan_started (shop ID) and navigation_started (shop ID and name).

Android shortens every event parameter to 100 characters before sending it. iOS does not, which matters for none of the parameters above, since none of them carries text you typed.

A change already under way: from the next releases — Android 1.0.7 and the next iOS build — the shop-name parameter is removed from all events (the shop ID remains) and iOS's strain_viewed sends the strain ID instead of the name; everything above describes the builds currently shipping.

3.3 What happens to your menu photos

Before the camera opens, on both platforms, the App tells you that an AI reads the photo and asks you to keep faces, ID documents and vehicle plates out of the frame, and then asks you to confirm. You can cancel at that point. Nothing is captured or sent unless you confirm. Then:

Does Google train on your photo? We are not going to tell you it does not, because we have not established that we are entitled to say so. Google's Gemini API Additional Terms treat data use differently across service tiers and customer establishments, and which of those apply to our API key is not something the App's source code shows. Version 2.1 stated flatly that images are never used to train the model. We have removed that claim rather than repeat it unverified. If you would rather no photograph of yours reached Google at all, do not use Scan Menu: nothing is sent unless you start a scan and confirm.

3.4 What we deliberately do not collect

Version 2.1 also claimed we collect no "record of which coffeeshop you visited". That claim was not correct when Analytics is switched on, and has been removed. See §3.5.

3.5 Cannabis, coffeeshops and special-category data

We never ask about your cannabis use, your health, or what you buy. We hold no purchase record and no visit log, and your location never reaches our servers.

But we will not tell you that no inference is possible. If you turn Analytics on, the events in §3.2 include the names of coffeeshops you open, favourite, ask directions to and photograph a menu at, and the strain pages you read — all under one per-installation identifier held by Google, kept for the analytics retention period. Photographing a menu board in particular can normally only happen while you are physically at that shop.

EU case law applies a broad test: data can fall under Art. 9(1) GDPR if it is liable to reveal a special category by inference, not only if it states one (CJEU C-184/20 OT, 1 Aug 2022; C-252/21 Meta Platforms, 4 Jul 2023; C-21/23 Lindenapotheke, 4 Oct 2024). Whether interest in a coffeeshop is "data concerning health" under Art. 4(15) GDPR has not been decided by any court or regulator. We are not going to claim the answer is obviously no, and we are not going to claim it is obviously yes. What we can tell you is exactly what is sent, which is the table above.

What this means for you in practice: if you do not want that stream to exist, leave Analytics off. It is off by default and refusing it costs you nothing — every feature of the App works exactly the same. Crash reporting carries the same signal in a smaller way, through the two Crashlytics keys named in §3.2, and it is off by default too.

We have taken external advice on whether to remove the shop identifier and shop name from those events entirely. Until that is settled, the position is the one described here rather than a promise we have not implemented.

3.6 Data about people who are not Hazely users

Some data we hold is about people who never used the App. Art. 14 GDPR applies to it, and this section is that notice.

Whose dataWhatWhere it came fromWhy we hold itBasisHow long
Authors of Google reviewsReviewer display name, rating, review text, relative timeThe Google Places API, refreshed by a daily jobSo users can see what a shop is like before walking thereArt. 6(1)(f) — our and our users' interest in accurate, useful public listingsNo retention limit is implemented today. Reviews are carried forward on every sync and are re-served until we delete them
People visible in shop photosPhotographs from public shop listings, served through our image proxyGoogle PlacesIllustrating a listingArt. 6(1)(f)Same as above
Bystanders in a menu photoAnything else that happens to be in the frameYou, when you scan a menuNot wanted — this is why we ask you to check the frame before the camera opensArt. 6(1)(f), and minimisation by asking you not to capture itThe image is never stored; only extracted menu text is kept, for up to 30 minutes

Two things we would rather be able to write differently, stated as they are:

If you are a review author or appear in a photo and want that removed, email privacy@hazely.nl. We will remove it from our cache. We cannot remove it from Google.

4. Purposes, lawful bases, recipients and retention

4.1 The full table

Consent-based rows are marked Consent. Where the operation also stores information on, or reads information from, your device, Art. 11.7a Telecommunicatiewet requires that consent separately from the GDPR — that is noted as (+ Tw 11.7a).

PurposeData usedLawful basisRecipientsRetention
Confirm you are 21+Age flag, timestampArt. 6(1)(f) — legitimate interest in keeping a cannabis-related app away from minors, and in meeting Google Play and Apple age requirementsNobodyUntil you uninstall or reset
Record what you accepted and what you consented toTerms version, consent version, three consent booleansArt. 6(1)(f) — legitimate interest in being able to demonstrate consent, as Art. 7(1) GDPR requiresNobodyUntil you uninstall or reset
Show the map, shop list, opening hours and ratingsYour request, your IP addressArt. 6(1)(f) — providing the service you opened the App to useGoogle (hosting, logs); Mapbox (tiles)Logs: see §7
Show your position and measure distanceForeground locationArt. 6(1)(f), plus your OS-level permissionNobody (stays on the device)Discarded when the App closes
Detect that you are within 50 m of a coffeeshopForeground location, shop coordinatesArt. 6(1)(f) — showing the arrival card and nearby-food suggestions you asked forNobodyHeld in memory only
Give walking directionsStart and destination coordinatesArt. 6(1)(f) — you asked for the routeMapbox Inc. (US)Governed by Mapbox
Mapbox Maps SDK telemetryDevice location and map-usage dataArt. 6(1)(a) Consent (+ Tw 11.7a) on Android, where it rides the Analytics switch. On iOS the App does not gate it, and you opt out through Mapbox's own controlMapbox Inc. (US)Governed by Mapbox
Extract text from a menu photoThe photo; optional shop ID; your IPArt. 6(1)(a) Consent — you start the scan and confirm the noticeGoogle (Cloud Functions, Gemini)Image: never stored. Result: see §7
Build strain catalogue entries we do not haveThe strain name you typedArt. 6(1)(a) Consent for sending your query; Art. 6(1)(f) for maintaining the catalogueGoogle (Cloud Functions, Gemini); the entry is then publicQuery appears in server logs; the generated entry is kept indefinitely
Product analyticsThe events in §3.2, app-instance ID, Advertising ID (Android)Art. 6(1)(a) Consent (+ Tw 11.7a)Google — Analytics for FirebaseSee §7
Crash reportingStack traces, device and app details, installation ID, selected-shop keysArt. 6(1)(a) Consent (+ Tw 11.7a)Google — Crashlytics; crash titles to GitHub, Inc.90 days at Google; GitHub issues until we delete them
Performance monitoringApp start, screen rendering, network request traces, installation ID, IPArt. 6(1)(a) Consent (+ Tw 11.7a) — but controlled by the Analytics switch, with no separate controlGoogle — Performance MonitoringSee §7
Feature flags and the "update required" kill switchFirebase installation ID, app version, platform, language, countryArt. 6(1)(f) — keeping the App working and being able to disable a broken or unsafe build. For the device read/write we rely on the "strictly necessary" exemption in Art. 11.7a(3)(b) TwGoogle — Remote ConfigGoverned by Google
Anti-abuse attestation of the app and devicePlay Integrity / App Check tokenArt. 6(1)(f) — security. Device read relies on Art. 11.7a(3)(b) TwGoogle — App Check, Play IntegrityGoverned by Google
Rate limiting and abuse preventionFull IP address as the rate-limit keyArt. 6(1)(f) — Recital 49 GDPRHeld in our function's memoryFor the life of the server instance
Server request logsFull IP, request URL (which includes strain search text), user agentArt. 6(1)(f) — operating and securing the serviceGoogle — Cloud Logging, Firebase HostingSee §7
Owner verification and correction requestsShop name, contact name, email, optional phone, messageArt. 6(1)(f) — accurate public listings; Art. 6(1)(b) where you are asking us to act on your requestGoogle (Firestore); our mail provider if you write to usSee §7
Illegal-content and abuse reportsMessage, optional emailArt. 6(1)(f) — safety and abuse handling; Art. 6(1)(c) to the extent a notice-and-action duty applies to us in lawGoogle (Firestore); our mail providerSee §7
Keeping shop listings accuratePublic listing data from Google Places and OpenStreetMap, including reviewer names and review textArt. 6(1)(f) — see §3.6Google (Places, Firestore)See §3.6
Asking you to rate the AppOn-device countersArt. 6(1)(f)Google Play / Apple show the promptDevice only
AdvertisingAdvertising ID, TCF consent stringArt. 6(1)(a) Consent (+ Tw 11.7a)Google AdMobNot applicable — no advertising runs today. See §4.3

Where we rely on legitimate interests (Art. 6(1)(f)), the interests are: giving you a working map and directions; keeping public listings accurate; keeping the service secure and available; being able to demonstrate the consent you gave; and keeping minors out of a cannabis-related app. You can object to any of it under Art. 21 GDPR — see §9.

Two bases that changed since version 2.1, and why it matters to you. Version 2.1 gave Art. 6(1)(c) (legal obligation) for the age gate and the acceptance log. Art. 6(3) GDPR requires such an obligation to be laid down in Union or Member State law, and there is none here: the Opiumwet tolerance criteria bind the coffeeshop rather than an information app, and the age there is 18, while our 21+ rule comes from app-store policy. We now rely on Art. 6(1)(f) instead. The practical difference is in your favour: Art. 6(1)(c) processing carries no right to object, and Art. 6(1)(f) processing does.

On Remote Config and App Check. Neither runs before you have answered the consent screen, on either platform — that is a hard gate in the code, not a convention, and it holds whatever you answered, because the "update required" kill switch has to keep working for someone who declined everything optional. For the on-device storage those two use after that point, we rely on the strictly-necessary exemption in Art. 11.7a(3)(b) Tw. That assessment is our own and has not been confirmed by an external adviser or by a regulator; we say so rather than present it as settled.

4.2 Do you have to give us this data? (Art. 13(2)(e) GDPR)

DataDo you have to?If you decline
Age confirmationYes, to use the AppOn Android the App stops there. There is no other way in
Accepting the Terms and this policyYes, to use the AppThe App does not proceed
Consent to analytics, crash reporting, adsNo — all three are optionalEverything in the App works exactly the same. This is the default
Location permissionNoThe map works. You lose the location dot, walking distance and navigation
CameraNoYou cannot use Scan Menu. Nothing else changes
Owner form / report form fieldsOnly if you choose to submit oneThe form cannot be submitted without the required fields

Nothing here is a statutory or contractual requirement to provide data, except that we cannot lawfully or practically operate the App for someone who will not confirm they are 21+.

4.3 Advertising and partner links, as they actually stand today

No advertising runs in Hazely. The Android app contains the Google Mobile Ads (AdMob) SDK and Google's certified consent management platform (UMP), but every one of the six ad placements is switched off, both in the app's built-in defaults and in the published server configuration. No ad is requested, no ad consent form is ever shown, the ads SDK is never started, and no ad has ever been served. The iOS app has no ads SDK at all.

No affiliate or partner monetisation runs either. The "food nearby" card that can appear when you arrive at a shop carries no partner link today: the feature is switched off and the partner URL is empty. We are not in any affiliate programme, and we receive no commission from anything in the App.

Not currently applicable. If advertising is ever switched on, Google's consent form will be shown before any personalised ad is served, non-personalised ads will be used if you refuse, and this policy will be updated before that happens rather than after. You should also know that for advertising Google positions itself as an independent controller, not as our processor, so it would process that data for its own purposes under its own privacy policy. Version 2.1 described AdMob as a processor under Art. 28 GDPR. That was wrong. None of this paragraph describes anything happening today.

5. Recipients — who else handles your data

RecipientWhat they do for usRoleWhere
Google — Cloud Functions & FirestoreRuns our backend and databaseProcessoreurope-west1 / eur3 — in the EU
Google — Firebase HostingServes hazely.nl and our API edgeProcessorGlobal edge; logs at Google
Google — Gemini Developer APIAI text extraction from menu photos; generating strain entriesProcessorGlobal endpoint
Google — Analytics for FirebaseProduct analytics (only with your consent)Processor, under Google's separate Analytics/Measurement terms rather than the general Firebase termsGlobal Google infrastructure
Google — CrashlyticsCrash reports (only with your consent)ProcessorGlobal
Google — Performance MonitoringPerformance traces (only with your consent)ProcessorGlobal
Google — Remote Config, Installations, App Check / Play IntegrityFeature flags, kill switch, anti-abuse attestationProcessorGlobal
Google — Cloud LoggingServer request logsProcessorGoogle infrastructure
Google — Places APIPublic shop listing data, ratings, reviews, photosProcessor for the enrichment; the data itself is Google'sServer-to-server — your IP is never exposed to Google Places by the App
Microsoft — Exchange Online (Microsoft 365)Receives and stores everything you email to privacy@, dpo@, legal@, security@ and abuse@hazely.nl, including the contents of rights requests and abuse reports. The hazely.nl mail exchanger points at Microsoft's mail serviceProcessorMicrosoft mail infrastructure
Google Play / AppleThe in-app "rate this app" prompt, and on Android the in-app update checkIndependent controllers for their own store data—
Google AdMob and Google UMPAdvertising — not active; nothing is sentIndependent controller for ad data, not our processor—
Mapbox Inc.Map tiles and styles, walking directions, and the Maps SDK's own telemetryProcessor for tiles and routing; its role for SDK telemetry is not settled and we have not determined itUnited States
GitHub, Inc. (Microsoft)Receives crash titles as issues in our private bug trackerProcessorUnited States

Removed in this version: Anthropic, PBC and Hetzner Online GmbH / the current VPS provider were both named as processors in version 2.1. Neither receives any data. There is no Anthropic call anywhere in our systems, and we no longer run a VPS. GoDaddy was listed as our domain registrar; a registrar does not process your personal data and has been removed.

On processing agreements. Version 2.1 stated that every processor is "contractually bound by a Data Processing Agreement under Art. 28 GDPR". We do not repeat that, because we have not verified it for every recipient in the table above, and stating it would be easier than checking it. Where a recipient is not a processor at all we now say so — AdMob is an independent controller for ad data, and Google Play and Apple are independent controllers for their store data. We are working through the remainder; when it is settled this section will say which terms cover which recipient, per recipient, rather than in one sentence.

We do not sell your data to anyone, ever.

6. International transfers (GDPR Chapter V)

Some of the recipients above process data outside the European Economic Area. Being accurate about which ones matters, and version 2.1 was not.

Where the data actually isWhich services
Inside the EUCloud Functions and Firestore (europe-west1 in Belgium, eur3 multi-region). This is where menu-scan results, owner requests and reports live
Global Google infrastructure — transfers outside the EEA occurAnalytics for Firebase, Crashlytics, Performance Monitoring, Remote Config, Installations, App Check, Cloud Logging, and the Gemini Developer API endpoint
United StatesMapbox Inc.; GitHub, Inc.
Microsoft mail infrastructureEmail sent to our published addresses

Version 2.1 said Firebase Analytics and Crashlytics were processed in "EU (Frankfurt region)" and that Gemini ran in "an EU region selected". Neither is correct. Google's own documentation (Privacy and Security in Firebase, page last updated 18 February 2026) states that most Firebase services run on global Google infrastructure with no data-location choice, and the Gemini Developer API endpoint we call offers no regional selection.

For transfers outside the EEA we rely on the safeguards in each provider's own terms — the Standard Contractual Clauses modules those terms incorporate, and/or the provider's certification under the Data Privacy Framework:

We say "rely on" here the way §5 says it about processing agreements: we have not independently verified, recipient by recipient, which of these instruments is actually in place for each transfer. The protections we can verify are the practical ones described in §3 and §5: an EU processing region where the provider offers one, and sending as little as possible in the first place.

Ask us at privacy@hazely.nl for a copy of the safeguards.

7. How long we keep things

DataRetention
Age flag, acceptance record, consent choices, favourites, theme, cached strain list, review-prompt countersOn your device until you uninstall the App or use "Reset all my privacy choices"
Search text you type into the mapNever leaves your device
Menu photo (the image)Never stored. Held in server memory for the length of the request
Menu scan result (the extracted text)Up to 30 minutes, then deleted. Two mechanisms enforce this: every record is written with an expiry timestamp and a Firestore time-to-live policy deletes it server-side whether or not anyone reads it again, and a record found to be expired is deleted and refused the moment it is read. Both are live. One caveat: a small number of records created before the expiry field existed carry no expiry timestamp and are invisible to the automatic sweeper; those are being deleted by hand, as a one-off cleanup (August 2026)
AI-generated strain entries derived from scans and searchesKept indefinitely, as part of the public catalogue. They contain nothing about you
Owner verification / correction requestsNo automatic deletion is implemented, and we are not publishing a period we do not enforce. We delete them on request, and we delete them when they are no longer needed to keep a listing accurate. Version 2.1 published 24 months; nothing implemented it
Illegal-content and abuse reportsSame as above
Email you send to our published addressesHeld in the mailbox until we delete it. We delete correspondence we no longer need to answer you or to show we answered
Crash reports90 days at Google (Firebase published retention)
Crash issues in our bug tracker (GitHub)Until we close and delete them
Analytics eventsAt most 14 months — that is the longest Analytics for Firebase allows, and the shortest it offers is 2 months. The exact figure is a setting in Google's console rather than something the App controls; ask privacy@hazely.nl if you need the figure currently in force
Performance dataGoogle's published retention: 30 days for events connected to an IP address, 60 days for installation-linked and anonymised performance data
Firebase installation identifiersGoogle deletes them within 180 days of a deletion request
Server request logs (including full IP addresses)Google Cloud Logging's default retention for the standard log bucket is 30 days, and nothing in our configuration changes it

Version 2.1 also promised an "audit log of AI API calls" kept for 90 days, "nightly database backups" kept 14 days, and irreversible deletion "within seven days" of a retention period ending. None of those controls exists. They have been removed rather than restated.

8. Automated decisions, profiling and AI features

8.1 Automated decision-making (Art. 22 GDPR)

We make no automated decisions that produce legal effects on you or similarly significantly affect you. Nothing in the App scores, ranks, segments or profiles you. Menu scanning reads text from a photo; it does not decide anything about you.

8.2 AI transparency (Art. 50, Regulation (EU) 2024/1689 — the EU AI Act)

Article 50 of the AI Act has applied since 2 August 2026. Two features of Hazely use AI, and this is our disclosure for both. A disclosure buried in a document like this one is not enough on its own, so both of them also appear in the App.

Menu scanning. When you tap Scan Menu, an AI system — Google's Gemini — reads the text in your photograph and turns it into a list. Results can be incomplete or wrong. Do not rely on a scan for prices, availability, or anything that matters. In the App:

AI-generated strain entries. If you search the strain catalogue for a strain we do not have, an AI system generates an entry — name, indica/sativa type, THC and CBD ranges, effects, flavours, parentage and a one-line description — and stores it in the shared catalogue for everyone. These entries are written by AI. They are not verified by a human, and they may be wrong.

Stated plainly, because it is the weakest point in our AI transparency: those entries are shown alongside human-curated ones without a visible "AI-generated" label, and the record carries no machine-readable marker of having been AI-generated — the field for one does not exist in our data format yet. Until both are in place, treat any strain entry in Hazely as possibly AI-written, and treat this section as the notice that it may be.

Neither feature is a high-risk AI system under Annex III of the AI Act.

9. Your rights

You have the following rights. They are free to exercise, and we answer within one month (extendable by two months for complex requests, Art. 12(3) GDPR).

RightArticleWhat it means here
Information13–14This policy
Access15A copy of the personal data we hold about you
Rectification16Correction of anything inaccurate
Erasure17Deletion. Uninstalling the App erases everything held on your device, and on Android that data is not in any Google backup
Restriction18Pausing our processing while a dispute is resolved
Portability20A machine-readable copy of data you provided under consent or contract. In practice that is your consent choices and the version you accepted, both already on your device
Object21Stop processing based on legitimate interests (§4.1)
Withdraw consent7(3)Any time, in Settings. Withdrawal does not undo processing that already happened
No automated decisions22Already the case — §8.1
Complain77To the Autoriteit Persoonsgegevens, https://autoriteitpersoonsgegevens.nl/en

How to make a request: email privacy@hazely.nl. The page at hazely.nl/data-request sets out what to include and what happens next; it is a page you can read, not a redirect.

One gap on our side, stated rather than hidden: the Android app has no in-app link to that page. The iOS app does, under Settings. On Android you reach it from hazely.nl or by emailing privacy@hazely.nl directly, which works just as well. The next Android release adds that link, together with an "App instance ID" display in Settings (with a copy button) so you can quote the identifier your analytics data is keyed to; the next iOS build adds the same ID display.

An honest limit on access, erasure and portability. We deliberately built the App without accounts and without any identifier of our own. That is good for your privacy, and it has a cost: for the analytics, crash and performance data held at Google, the records are keyed to a per-installation identifier that we cannot connect to you, and that the App does not show you. Where we genuinely cannot identify you, Art. 11(2) GDPR provides that Arts. 15–20 do not apply unless you give us information that makes identification possible.

What we can always do:

And the practical answer, which is better than any of them: if you do not want analytics, crash or performance records about your installation to exist at Google, leave those switches off. They are off unless you turn them on, and uninstalling the App discards the identifier they were keyed to.

10. Turning optional processing off

WhatHow
Analytics, Performance and crash reporting (Android)App → Settings → "Analytics & Performance". Be aware: this single control currently governs analytics, performance monitoring and crash reporting together. If you consented to only one of them, the switch shows as off, and turning it on turns on all three. That is a defect in our Settings screen, not the intended design, and it is why this warning is here rather than in a release note
Analytics, Performance and crash reporting (iOS)App → Settings — two independent switches: "Analytics & performance" (one switch controls both — Performance has no control of its own on iOS either) and "Crash reports"
Personalised ads (Android only)App → Settings → Personalised ads. The iOS App has no advertising SDK, serves no ads, and — following App Review feedback — no longer shows an ads switch at all. No ads are served today on either platform
Everything at onceApp → Settings → Reset all my privacy choices. This clears your choices and asks you again
Menu scanningDo not tap Scan Menu, or cancel at the notice. Nothing is sent unless you confirm
LocationAndroid: Settings → Apps → Hazely → Permissions → Location. iOS: Settings → Hazely → Location. Choose "While using the app" and, if you prefer, approximate accuracy
Mapbox map telemetryAndroid: it follows the Analytics switch above — turn Analytics off and the App switches Mapbox telemetry off too. iOS: the App does not gate it; tap the (i) / attribution control on the map and use Mapbox's own opt-out. Either way, Mapbox's monthly-active-user event still goes; the SDK gives nobody a way to stop that one
Everything on your deviceUninstall the App

11. Security

What we actually do:

Version 2.1 described an Nginx reverse proxy, a service bound to localhost:8080, a chmod 600 secrets file, unattended OS security updates, and truncation of IP addresses on receipt. That described a self-hosted server we no longer run. None of it applies to the current system, and the IP-truncation control does not exist in it.

No system is impenetrable. If a personal data breach occurs we will notify the Autoriteit Persoonsgegevens within 72 hours where required (Art. 33 GDPR).

How we would tell you. We have no accounts, no email address for you, and no push notifications. We therefore cannot contact affected users individually. Where a breach is likely to result in a high risk to your rights and freedoms, we will rely on Art. 34(3)(c) GDPR and make a public communication: a notice on hazely.nl and a notice inside the App. Version 2.1 promised to notify you directly. The App cannot do that.

12. Children and the 21+ age gate

Hazely is not intended for anyone under 21. On first launch we ask you to confirm you are 21 or older, and Android blocks the App if you say you are not.

This is a self-declaration. We cannot verify anyone's real age, and we do not try — verifying age would mean collecting identity documents, which would be far more intrusive than the problem it solves.

We do not knowingly process the data of anyone under 21. If you are a parent or guardian and believe someone under 21 has used the App, email privacy@hazely.nl. Almost everything the App holds is on the device itself, so uninstalling removes it immediately.

Version 2.1 described the age gate as "a technical measure under Art. 8 GDPR". That citation was wrong — Art. 8 governs a child's consent to online services below the age of 16 (set at 16 in the Netherlands by Art. 5 UAVG), which is not what a 21+ declaration is. It has been removed.

13. Changes to this policy

We will update this policy when what we do changes. The version and effective date shown with this document always identify the current text.

What re-prompts you, and what does not. The App's acceptance gate is keyed to the Terms of Use and the Cookie & Tracker Policy — the two documents you are asked to accept — and not to this Privacy Policy, which is information we owe you rather than something you agree to. Stated plainly for each platform:

Where a change here is material we publish it at hazely.nl/privacy and hazely.nl/privacybeleid and note it in the App's release notes.

Earlier versions. We do not publish an archive page. Ask privacy@hazely.nl for the text of any earlier version and we will send it to you. Version 2.1 promised an archive at hazely.nl/privacy/archive that never existed; the promise has been removed rather than left standing.

One text, several places. The English text of this policy is generated from a single source file into the copy bundled in the Android app and the page at hazely.nl/privacy, so those two cannot disagree. The Dutch text is generated the same way into hazely.nl/privacybeleid and into the Android app for devices set to Dutch. The App's own screen headings are still English even when the body text is Dutch. iOS opens the published web version rather than carrying a copy.

A limitation you should know about. Some text shown in the App — including wording on the age-gate and consent screens — can be replaced remotely through Firebase Remote Config without a new app release. We use this to fix wording quickly. It means we do not keep a snapshot of the exact words you were shown when you gave consent, and so we cannot demonstrate afterwards precisely what any individual user read. We are telling you because Art. 7(1) GDPR makes that our problem, not yours; nothing about it limits any right you have under §9.

14. Contact and complaints

Changelog

Version 3.0 — 4 August 2026 (replaces version 2.1 of 21 May 2026)

This is a major version because the previous version described a system we no longer run and, in several places, described data handling that did not match the app. Under Art. 5(1)(a) and Art. 13 GDPR a privacy notice has to be accurate, and correcting an inaccurate notice is itself a material change.

Who we are, at last:

1. The controller is now named — a natural person established in the Netherlands, trading under the name published on the Google Play listing. Version 2.1 pointed at "the publisher identified in the Google Play listing", which told an iOS user or a website reader nothing (§1). 2. No company registration is claimed, because there is none yet. When a Dutch business registration exists, §1 will be updated and this policy reissued (§1). 3. A postal address is available on request rather than published, and §1 and §14 both say so and both give the address to ask at (§1, §14). 4. dpo@hazely.nl is explained rather than left ambiguous: no Data Protection Officer has been designated, none is required, and the address is a routing alias (§1).

Corrections of statements that were not accurate:

5. Search text. Version 2.1 said search queries were "in-memory only — never transmitted". With Analytics on, the raw text of a map search used to be sent to Google. It no longer is: only the number of characters and the number of results are sent, on both platforms. The original claim is true again, and §3.4 states it in the narrow form that is actually true (§3.1, §3.2, §3.4). 6. Strain searches. Text typed into the strain catalogue is sent — to our server and on to Google Gemini when there is no local match. Previously undisclosed, and deliberately separated from the map search so the difference is visible (§3.1). 7. Hosting. Removed "Hetzner Online GmbH or the current VPS provider". Added Google Cloud Functions and Firestore in europe-west1, which is where the data actually is (§5, §6). 8. Anthropic. Removed. No Anthropic service is used; menu scanning uses Google Gemini only (§3.3, §5). 9. Data location. Removed the claims that Firebase Analytics and Crashlytics run in "EU (Frankfurt)" and that Gemini runs in a selected EU region. All three are global services (§6). 10. IP addresses. Removed the claim that IPs are truncated to /24 and /48 on receipt. They are not truncated anywhere in the current system (§3.2, §7, §11). 11. EXIF stripping. Version 2.1's unqualified promise was false at the time — an image the server could not decode was forwarded intact. The server now rejects such an upload, so the promise is true as written (§3.3, §11). 12. Menu-scan retention. "Server memory only" corrected to the actual database record, and the 30 minutes is now enforced by a time-to-live policy that deletes the record whether or not anyone reads it again (§3.3, §7). 13. The training claim about menu photos has been removed, not weakened. We do not state that Google does not train on your image, because we have not established that we may (§3.3). 14. "We collect no record of which coffeeshop you visited" — removed, because the analytics events contradict it. §3.5 now states the position honestly instead of arguing that Art. 9 GDPR cannot apply (§3.4, §3.5). 15. Security section rewritten for the Firebase architecture; the Nginx, localhost:8080, chmod 600 and unattended-upgrades description was removed (§11). 16. Breach notification reframed to a public communication under Art. 34(3)(c), because the App has no way to contact you individually (§11). 17. Retention promises removed that had no implementation: the AI-call audit log, nightly backups, the seven-day deletion service level, and the 24-month period for owner requests (§7). 18. AdMob re-described as an independent controller for advertising, not as a processor under Art. 28 GDPR — and the whole advertising section rewritten to say plainly that nothing runs today (§4.3, §5). 19. The blanket Art. 28 assurance was dropped. We do not claim every recipient is bound by a processing agreement, because we have not verified it for every recipient (§5). 20. Legal bases corrected: Art. 6(1)(c) removed for the age gate and the acceptance log; Art. 6(1)(f) used instead, which gives you a right to object that the old basis did not (§4.1). 21. Art. 8 GDPR citation removed from the children section (§12). 22. Feedback forms and crash-reporter comments removed — neither exists (§3.1). 23. The archive promise removed. hazely.nl/privacy/archive never existed; earlier versions are available on request instead (§13).

Newly disclosed processing that was previously missing:

24. Firebase Performance Monitoring, including its automatic network-request traces, and the fact that it has no control of its own (§3.2, §4.1, §10). 25. Firebase Remote Config and Firebase installation identifiers — now gated behind the consent screen on both platforms, where version 2.1's iOS build fetched at launch, before the age gate (§3.2, §4.1). 26. Firebase App Check / Google Play Integrity, which runs even if you reject everything optional (§3.2, §4.1). 27. The Google Play in-app update check, which runs before the age gate (§3.2). 28. GitHub, Inc. as a recipient of crash titles (§3.2, §5). 29. Microsoft Exchange Online as the recipient of everything emailed to our published addresses (§5, §6). 30. The Android Advertising ID, collected by the Analytics SDK under the Analytics switch rather than the ads switch (§3.2). 31. Mapbox Maps SDK telemetry — now switched off on Android unless you consented to Analytics, still not gated on iOS, and with the residual monthly-active-user event disclosed (§3.2, §4.1, §10). 32. The on-device 50-metre proximity check around coffeeshops (§3.2). 33. Permanent storage of AI-generated strain entries derived from scans and searches (§3.3, §7). 34. The full analytics event list, parameter by parameter (§3.2).

New sections:

35. §3.6 — an Art. 14 GDPR notice for people whose data we did not get from them: Google review authors, people in listing photos, and bystanders in menu photos, including the two things about it we cannot yet state as we would like. 36. §4 — a single table giving purpose, data, lawful basis, recipients and retention together, and §4.2, the Art. 13(2)(e) statement of what happens if you decline. 37. §8.2 — AI transparency under Art. 50 of the EU AI Act, applicable since 2 August 2026. Both platforms now show the AI notice before the camera opens; version 2.1's Android build showed it only with the result. 38. §9 — the Art. 11(2) GDPR limitation on access, erasure and portability, stated openly rather than promising rights the architecture cannot deliver. 39. §13 — an honest description of what re-prompts you on each platform, and the fact that in-app wording can be changed remotely.

Known defects we have chosen to publish rather than paper over: the merged Android "Analytics & Performance" switch (§10), the absence of version tracking on iOS (§13), the absence of an in-app data-rights link on Android (§9), the absence of a visible "AI-generated" label on generated strain entries (§8.2), the absence of a retention limit on cached shop reviews (§3.6), and the fact that consent-screen wording can be changed remotely (§13). Each of them is a thing to fix, and each is described here in the form it actually takes today.