Stockify — Privacy Notice
Last updated: 2026-06-24
This privacy notice applies to the Shopify app Stockify and describes which personal data is processed, for what purpose, on what legal basis, for how long, and with whom it is shared. It is kept in sync with the code — changes to the data model or to the sub-processor stack trigger an update to this file.
Timing and form of the information (Art. 13 GDPR). Pursuant to Art. 13(1) GDPR, this notice is provided to the merchant at the time the data is collected, i.e. at the latest when the app is installed:
- Shopify App Store listing: The public app listing references this URL (
https://stockify.amassimo.com/privacy) as the "Privacy Policy". It is visible before the install click. - Install flow: The Shopify OAuth consent screen links to the privacy URL stored in the app listing before the merchant confirms the app permissions.
This notice is also publicly accessible at any time at https://stockify.amassimo.com/privacy. Later updates are documented via the "Last updated" line above and via section 12. Change history. Material changes to the scope of processing are additionally communicated by email to the shop's stored email_address.
1. Controller
The controller within the meaning of Art. 4(7) GDPR is:
Antoine Massimo Grünebaumstraße 13 33098 Paderborn Germany
Email: [email protected]
Stockify is operated as a solo project. A data protection officer is not required under Art. 37 GDPR.
2. What data is processed
Stockify only stores shop-related configuration and inventory metrics in its own PostgreSQL database on the Stockify server. No identifying customer data (customer IDs, names, email addresses, shipping addresses, payment data) of the shop's end customers is stored. For the reorder forecast, Stockify reads historical orders using the read_orders scope, but persists from them only a net sales quantity aggregated per variant and per calendar day (units sold minus units refunded) — no order ID, no order value, no buyer reference. This data minimisation is deliberate: the aggregates cannot be attributed to any individual end customer.
| Table / field | Content | Personal reference | Legal basis |
|---|---|---|---|
shops.shop_domain |
Shopify shop domain (*.myshopify.com) |
indirect (shop owner) | Art. 6(1)(b) GDPR (performance of a contract) |
shops.plan, subscription_id |
Subscription status (Shopify Billing GID) | no | Art. 6(1)(b) |
shops.email_address |
Optional email address for reports/alerts | personal | Art. 6(1)(b) |
shops.smtp_host / smtp_port / smtp_user / smtp_secure |
SMTP configuration (opt-in for own mail server) | technical | Art. 6(1)(b) |
shops.smtp_pass |
SMTP password, AES-256-GCM encrypted | technical | Art. 6(1)(b) |
shops.discord_webhook_url / slack_webhook_url |
Optional webhook URLs for reports/alerts | technical (channel-bound bearer secret) | Art. 6(1)(b) |
shops.discord_report_days / discord_alert_enabled / slack_report_days / slack_alert_enabled |
Per-channel report schedule (weekdays) + instant-alert toggle | no | Art. 6(1)(b) |
shops.min_stock_default, email_report_days, email_alert_enabled, last_report_sent |
Configuration and status | no | Art. 6(1)(b) |
shops.locale, locale_override |
Language preference (DE/EN) | technical | Art. 6(1)(b) |
shops.installed_at, uninstalled_at, pricing_feedback_dismissed_at, default_min_stock_set_at, onboarding_dismissed_at |
Timestamps for app lifecycle and UI state | no | Art. 6(1)(b) |
sessions.* |
Shopify OAuth session: id, shop, state, isOnline, accessToken, scope, expires (always); additionally userId, firstName, lastName, email, accountOwner, locale, collaborator, emailVerified — stored if Shopify includes these fields in the OAuth response (regularly for online sessions, usually not for offline sessions). The fields are used to authenticate against the Shopify API; they are not analysed or enriched. |
personal (shop owner or staff member with app access) | Art. 6(1)(b) |
product_settings.* |
Minimum stock levels per product variant | no | Art. 6(1)(b) |
alert_state.* |
Last known stock status per variant (ok/low/out) for duplicate suppression on instant alerts |
no | Art. 6(1)(b) |
pricing_feedback.* |
Optional price feedback provided by the shop: willingness-to-pay bucket (wtp_bucket) + primary value driver (value_driver) + currency. Plus a server-captured shop snapshot: product, variant and location counts, the effective plan tier (plan), and a usage profile (features_used — which Stockify features the shop has activated). No free price amount, no customer data. |
no (shop-related configuration/usage data, no end-customer reference) | Art. 6(1)(a) GDPR (consent — the input is voluntary, not a mandatory field) |
feature_requests.message |
Optional free-text feature wish provided by the shop | may be personal if the shop writes such data into the free text — the UI hint explicitly asks not to | Art. 6(1)(a) GDPR (consent) |
suppliers.name / notes |
Suppliers created by the merchant (name, optional notes) | technical; may be personal if the merchant enters a natural person as a supplier | Art. 6(1)(b) |
suppliers.email / phone |
Optional supplier contact details | personal (contact details of the supplier/contact person) | Art. 6(1)(b) |
purchase_orders.* |
Purchase orders created by the merchant: reference, notes, status, currency, timestamps, supplier link | no | Art. 6(1)(b) |
purchase_order_lines.* |
Order line items: variant ID + snapshot of product title/variant title/SKU, ordered and received quantities, optional unit cost | no | Art. 6(1)(b) |
purchase_order_receipts.* |
Idempotency receipt per booked goods receipt (shop domain, order link, idempotency key, timestamp) — prevents double-booking on receiving | no | Art. 6(1)(b) |
forecast_sales_daily.* |
Net sales quantity aggregated per variant and calendar day (units sold minus units refunded), derived from read_orders. No order ID, no order value, no buyer reference |
no (not attributable to an end customer) | Art. 6(1)(b) |
forecast_inventory_snapshots.* |
Daily available-stock level per variant (for stockout censoring of the forecast) | no | Art. 6(1)(b) |
forecasts.* |
Computed forecast metrics per variant (daily sales, dispersion, method, confidence, lead time, safety stock, reorder point) | no | Art. 6(1)(b) |
forecast_seasonality.* |
Shop-wide monthly seasonal index pooled across all variants | no | Art. 6(1)(b) |
suppliers.lead_time_days |
Optional lead time (days) per supplier, feeds into the forecast's reorder point | no | Art. 6(1)(b) |
product_settings.forecast_excluded |
Flag whether a variant is excluded from the forecast | no | Art. 6(1)(b) |
shops.forecast_lead_time_days / forecast_service_level / forecast_orders_synced_through / forecast_orders_access |
Shop forecast defaults + sync cursor (up to which point orders were aggregated) + status of the read_orders grant (granted/pending, drives the notice banner on the forecast page) |
no | Art. 6(1)(b) |
shops.multi_location_enabled |
Toggle whether the shop enabled multi-location mode | no | Art. 6(1)(b) |
location_settings.* |
Minimum-stock override per (variant, Shopify location) | no | Art. 6(1)(b) |
purchase_orders.location_id / location_name |
Optional destination location of a purchase order (Shopify location id + display name) | no | Art. 6(1)(b) |
stock_transfers.* |
Log of transfers between locations: source/destination location (id + name), status, note, timestamp | no | Art. 6(1)(b) |
stock_transfer_lines.* |
Transfer line items: variant id + snapshot of product/variant title/SKU, quantity | no | Art. 6(1)(b) |
2.1 What is not stored
- No identifying customer data of the shop's end customers (customer IDs, names, emails, shipping/billing addresses, payment data). Orders are read for the forecast (
read_orders) but not stored as orders — only the net sales quantity aggregated per variant and day is persisted (seeforecast_sales_dailyabove), which cannot be attributed to any individual end customer - No raw order data — order IDs, order values, line-item prices, buyer references are never persisted; they are processed in memory during the nightly aggregation and immediately discarded
- Stock levels are queried live via the Shopify GraphQL API on demand for display. For the forecast, Stockify additionally keeps a daily stock snapshot per variant (
forecast_inventory_snapshots) in order to exclude sold-out days from the sales estimate. On goods receipt, Stockify credits the delivery quantity booked by the merchant to the variant's available stock via the Shopify Admin API (scopewrite_inventory) — only upon an explicit merchant action, no automatic write access - No payment data — payments are handled exclusively via Shopify Billing; Stockify only stores the non-sensitive
subscription_id(Shopify GID) - No tracking, analytics or telemetry data
2.2 Consent for voluntary inputs (Art. 6(1)(a))
The data marked with (a) in the table above (pricing_feedback.*, feature_requests.message) is processed solely on the basis of the merchant's voluntary consent:
- Voluntariness: Both inputs are not a prerequisite for using the app. All core functions (minimum stock, reports, alerts) are fully usable without providing this feedback.
- Act of consent: Consent is given by actively filling in the respective field and clicking the submit button. The UI hint right at the form states the voluntary nature and the right of withdrawal.
- Documentation (Art. 7(1) GDPR): Each entry automatically receives a
created_attimestamp; together with the shop domain this evidences the when and who of the consent. - Withdrawal (Art. 7(3) GDPR): Consent can be withdrawn at any time with effect for the future — most easily directly in the app under Settings (button "Withdraw entry" for the price feedback or "Delete my wishes" for the wish field), alternatively informally by email to [email protected]. The entries are associated exclusively via the shop domain (no email address, no name stored); to locate and process your entries I therefore need you to state your
*.myshopify.comdomain (identity verification under Art. 12(6) GDPR). Upon request, the affected entries are promptly deleted or anonymised (removing the shop association; after that the remaining, purely statistical values are no longer personal data, Recital 26). Withdrawal does not affect the lawfulness of processing carried out up to that point. On app uninstall, these entries are additionally deleted automatically via the Shopifyshop/redactwebhook (48 h after uninstall).
2.3 Data flow
- Incoming (Shopify → Stockify): OAuth token on installation; product and inventory data queried live (not persisted); for the forecast, orders queried nightly (
read_orders) and immediately condensed into daily/variant aggregates (raw order data is not persisted); webhook notifications (inventory_levels/update,app/uninstalled,app_subscriptions/update, GDPR webhooks). - Outgoing (Stockify → recipients): Weekly inventory reports and instant alerts to the channels configured by the shop (email / Discord / Slack). Content: list of the respective shop's critical products (title, SKU, stock, minimum stock).
- Outgoing to owner: On wish submits (
feature_requests), Stockify sends a content-free trigger ping without shop identification or free-text content to an owner Discord webhook. Content: only "a new entry exists". The owner reads the actual content in the local database.
3. Retention
| Data | Retention | Deletion trigger |
|---|---|---|
sessions.* |
Deleted immediately on app/uninstalled webhook |
Webhook (triggered by Shopify as soon as the app is uninstalled in the shop) |
shops.*, product_settings.*, alert_state.*, pricing_feedback.*, feature_requests.*, suppliers.*, purchase_orders.*, purchase_order_lines.*, purchase_order_receipts.*, forecast_sales_daily.*, forecast_inventory_snapshots.*, forecasts.*, forecast_seasonality.*, location_settings.*, stock_transfers.*, stock_transfer_lines.* |
Until shop/redact webhook (regularly triggered by Shopify 48 h after uninstall); fallback: 90 days from uninstalled_at via the defense-in-depth cleanup job |
shop/redact webhook, or daily cleanup job |
forecast_sales_daily.*, forecast_inventory_snapshots.* (ongoing operation) |
Rolling window of max. 400 days; older aggregates/snapshots are deleted automatically on each nightly run | Nightly forecast job |
| Backups (snapshots of the Postgres DB) | 30 days rolling, locally and as an encrypted off-site copy at Cloudflare R2; overwritten automatically thereafter | Rotation script |
| Server logs (pino, structured) | 30 days rolling, rotated thereafter | Logrotate |
GDPR webhook bodies and inventory_levels/update payloads are generally not written to logs (see section 6).
3.1 Residual data in backups and logs
After the live database is deleted via shop/redact or the defense-in-depth cleanup job, shop-related data may, for technical reasons, still be present in backups and logs for the respective rotation period:
- Postgres backups: up to 30 days — both in the local backup copy and in the encrypted off-site copy at Cloudflare R2 (see section 8) —, overwritten by the rotation script thereafter.
- Server logs: up to 30 days, overwritten by Logrotate thereafter.
This residual retention is technically required to ensure recoverability and error diagnosis (Art. 32 GDPR — integrity and availability as an aspect of security of processing) and ends automatically through the rotations mentioned, without any further action. Selective deletion of individual shop data from backup snapshots is not technically practicable; targeted restoration of deleted data from backups does not take place.
Upon a justified access request under Art. 15 GDPR, the existence of such residual backup/log content is transparently named; the specific deletion date follows from the respective rotation period.
4. GDPR webhooks (mandatory Shopify implementation)
Stockify implements all three GDPR webhooks required by Shopify:
customers/data_request: Responds with200 OK. No data attributable to an end customer is stored — the sales quantities derived for the forecast are aggregated per variant and day and carry no buyer reference, so they cannot be attributed to any individual customer. There is therefore no personal data to disclose. The request body is not logged.customers/redact: Responds with200 OK. No data attributable to an end customer exists for deletion (the aggregated sales quantities carry no buyer reference). The request body is not logged.shop/redact: Deletes all data belonging to the shop fromsessions,product_settings,alert_state,pricing_feedback,feature_requests,suppliers,purchase_orders,purchase_order_lines,purchase_order_receipts,forecast_sales_daily,forecast_inventory_snapshots,forecasts,forecast_seasonality,location_settings,stock_transfers,stock_transfer_linesandshops. Technically, a singleDELETE FROM shopssuffices — all shop-related tables hang offshopsviaFOREIGN KEY … ON DELETE CASCADE(order lines cascade transitively via their order), so the supplier contact details and forecast aggregates are structurally deleted along with them.
In addition:
app/uninstalled: Marks the shop as uninstalled (uninstalled_attimestamp) and deletes all sessions immediately.- Defense-in-depth cleanup: A daily background job (02:30 Europe/Berlin) deletes all shop data whose
uninstalled_atis older than 90 days — in caseshop/redactnever arrives.
5. Encryption
- At rest:
shops.smtp_passis encrypted with AES-256-GCM; the key resides in an environment variable (ENCRYPTION_KEY), not in the database. The forecast aggregates derived fromread_orders(forecast_sales_daily,forecast_inventory_snapshots) are not personal data (Recital 26 — no end-customer reference) and are stored without additional application-level encryption; access to the database is restricted to the controller (solo operation, see section 1). - In transit: All communication with Shopify and processors runs over HTTPS / TLS 1.2+. The SMTP connection to the mail server (Brevo or the shop's own server) uses STARTTLS (port 587) or TLS (port 465).
- Ingress: The Stockify server is reachable exclusively via a Cloudflare tunnel; there is no open port on the host. Cloudflare terminates TLS at the edge and forwards traffic encrypted to the origin.
6. Logs
Logs (structured via pino) contain no personal data:
- GDPR webhook bodies are not logged
inventory_levels/updatepayloads are not logged (the shop's trade secret)- OAuth tokens are never emitted (only their existence is noted in debug logs)
- Free-text content of wishlist entries is not logged
7. Cookies and client-side storage (§ 25 TTDSG)
Stockify sets no cookies. The app interface runs embedded in the Shopify admin and uses only sessionStorage for the duration of the respective browser session:
| Key | Content | Purpose | Lifetime |
|---|---|---|---|
sessionStorage.shopify_host |
host parameter passed through by Shopify (Base64-encoded) |
Required by App Bridge to communicate with the Shopify admin | Until the tab is closed |
sessionStorage.shopify_shop |
Current shop domain | Required for authenticated API calls to the Stockify server | Until the tab is closed |
sessionStorage.shopify_locale |
Language code (de / en) |
Required for the correct UI language after re-mounts | Until the tab is closed |
These storage operations are strictly necessary for operating the app in the Shopify admin and are therefore exempt under § 25(2) No. 2 TTDSG — no consent is required. There is no tracking, no profiling and no disclosure to third parties.
localStorage is not used.
8. Processors and third-country transfer
Stockify uses the following external service providers as processors under Art. 28 GDPR:
| Provider | Purpose | Location | Data processed | Legal basis for third-country transfer | Contract |
|---|---|---|---|---|---|
| Shopify International Ltd. | Platform (app-hosting context, OAuth, webhooks, billing) | Ireland (EU) / Canada | OAuth token, shop domain, shop-owner metadata | Group DPA with SCC for the Canada portion | Shopify Partner Program DPA |
| Brevo (Sendinblue SAS) | Transactional mail delivery of reports/alerts via managed SMTP | France (EU) | Recipient address (email_address), mail content (list of critical products) |
EU-internal, no third-country transfer | Brevo standard DPA |
| Cloudflare, Inc. | Ingress (TLS termination + named tunnel to the origin server), optional email routing for *@stockify.amassimo.com, and encrypted off-site backup storage (Cloudflare R2) |
USA (EU adequacy measures) | TLS-terminated cleartext traffic between edge and origin (short-lived); email forwarding (privacy@); client-side encrypted database backups (not readable by Cloudflare) | Art. 45 GDPR in conjunction with the EU-US Data Privacy Framework + SCC | Cloudflare DPA |
8.1 Notes on the processors
- Brevo is used exclusively for transactional mail delivery — no marketing, tracking or profiling function. Shops that configure their own SMTP server in the Stockify settings bypass Brevo entirely — in that case the recipient and mail content are neither transmitted to nor processed by Brevo.
- Cloudflare naturally sees the TLS-terminated data stream between edge and origin. All Stockify traffic runs over a Cloudflare tunnel; there is no alternative in the current setup. For the owner mailbox
[email protected], Cloudflare Email Routing is additionally used — mail to this address is forwarded to the controller's personal mailbox. Cloudflare processes these mails only for delivery and does not store them beyond the forwarding. - Cloudflare R2 (off-site backups): The nightly database backups are synced to Cloudflare R2 as an additional off-site copy. The backups are encrypted client-side before upload (
rclone crypt); the decryption key remains exclusively with the controller and is not made accessible to Cloudflare. Cloudflare therefore stores only a ciphertext that is unreadable in content to it and acts as a mere storage location. - Obligations of the processors: All of the above processors are contractually obliged to comply with the GDPR, may only process the data on instructions, and are subject to confidentiality and security requirements under Art. 28(3) GDPR.
8.2 Recipients designated by the merchant (Discord / Slack)
If a shop stores a Discord or Slack webhook in the Stockify settings, Stockify transmits the report and alert content (shop domain, product titles, SKU, stock levels) to the webhook endpoint chosen by that shop.
This transmission does not take place as processing on behalf of Stockify: the recipient and the channel are determined and configured solely by the merchant; Discord or Slack act in this respect as third-party recipients designated by the merchant of the merchant's own communication service. Stockify is here a mere transmission tool acting on the merchant's instructions. The controller within the meaning of Art. 4(7) GDPR for these data flows is the respective merchant, not Stockify; in particular, the merchant bears responsibility for any of its own data processing agreements with Discord (USA, EU-US Data Privacy Framework) or Salesforce/Slack (USA, EU-US Data Privacy Framework).
The recipient URL is stored in the Stockify database as the shop's technical configuration (see the table in section 2) and used exclusively to send the reports and alerts activated by the merchant.
8.3 Owner trigger pings to Discord (internal, no personal reference)
On a new entry in the wishlist/feature requests, Stockify sends a content-free trigger ping to a private Discord webhook of the controller. The payload is a hard-coded constant text:
- Wishlist:
"A new wish was submitted. Review it in the Stockify database."+ timestamp
No shop identifiers, no free-text content, no recipient mails and no other personal data are transmitted to Discord. The controller reads the actual content of the wishlist entries manually in the local Stockify database.
Since no personal data is processed with these pings, there is no application of Art. 28 GDPR in this respect; Discord is here not a processor but a technical communication channel for the controller's internal operational organisation.
9. Data subject rights
You have the right at any time to:
Access the data stored about you (Art. 15 GDPR)
Rectification of inaccurate data (Art. 16 GDPR)
Erasure of your data (Art. 17 GDPR) — "right to be forgotten"
Restriction of processing (Art. 18 GDPR)
Data portability in a structured, commonly used, machine-readable format (Art. 20 GDPR)
Object to processing (Art. 21 GDPR)
Withdraw consent given with effect for the future (Art. 7(3) GDPR) — applies above all to the voluntary entries in pricing feedback and wishlist
Lodge a complaint with a data protection supervisory authority (Art. 77 GDPR). Competent for the controller:
State Commissioner for Data Protection and Freedom of Information of North Rhine-Westphalia (LDI NRW) Kavalleriestraße 2–4 40213 Düsseldorf, Germany https://www.ldi.nrw.de
You may also contact the supervisory authority of your habitual residence.
Please address requests concerning your rights informally to [email protected]. For shop data, the Shopify-native customers/data_request / customers/redact / shop/redact mechanism is also available (see section 4).
10. Transfer to third countries
Within Stockify's sphere of responsibility, a transfer to third countries takes place exclusively to the processors named in section 8. Specifically:
- Cloudflare, Inc. (USA) — ingress, email routing and encrypted off-site backup storage (R2; unreadable in content to Cloudflare). Legal basis: Art. 45 GDPR in conjunction with the EU-US Data Privacy Framework (adequacy decision of the EU Commission of 10 July 2023), supplemented by EU Standard Contractual Clauses (SCC) of the EU Commission of 4 June 2021 as a fallback.
- Shopify (Canada portion) — group DPA with SCC.
Brevo processes exclusively within the EU. A list of certified US recipients is available at https://www.dataprivacyframework.gov/.
If the merchant additionally designates its own recipients in the USA (Discord webhook, Slack webhook — see section 8.2), the transfer there takes place within the merchant's sphere of responsibility. Stockify merely executes this transfer technically on the merchant's instructions.
11. Complaint and breach notification
Should a data breach (Art. 33 GDPR) occur despite all technical and organisational measures, it will be reported to the LDI NRW within 72 hours of becoming aware of it. Affected shops will — where there is a high risk (Art. 34 GDPR) — be notified via the stored email_address.
12. Change history
| Date | Change |
|---|---|
| 2026-06-24 | Drift correction after a privacy audit: pricing_feedback (§2) updated to the size-based model from migration 006 (willingness-to-pay bucket + value driver + server-captured shop snapshot incl. plan and features_used/usage profile) — the old "free price amount" description was outdated. shops.forecast_orders_access (migration 007) and purchase_order_receipts (migration 005) added in §2 and reflected in retention (§3) and shop/redact deletion scope (§4). §5 extended with the at-rest classification of the read_orders aggregates (non-personal, Recital 26). |
| 2026-06-22 | Feature "Multiple locations & transfers" (opt-in): new tables location_settings, stock_transfers, stock_transfer_lines plus columns shops.multi_location_enabled and purchase_orders.location_id/location_name added in section 2 — location infrastructure (location ids/names) and variant/quantities only, no customer data/PII whatsoever. Retention (section 3) and shop/redact deletion scope (section 4) extended accordingly (structural cascade deletion). No new Shopify scopes — read_locations was already granted, transfers use the existing write_inventory. |
| 2026-06-17 | Feature "Reorder forecast" (forecasting): added scope read_orders (protected customer data) — only aggregated net unit counts per variant/day plus stock snapshots are stored (tables forecast_sales_daily, forecast_inventory_snapshots, forecasts, forecast_seasonality), no customer data/PII whatsoever; sections 2, 3 and 4 extended accordingly, GDPR webhooks (section 4) clarified regarding the PII-freeness of the aggregates. Backup details brought up to the real state: 30-day retention plus an encrypted off-site copy at Cloudflare R2 (sections 3, 3.1, 8, 8.1, 10). |
| 2026-06-17 | Feature "Purchase orders & suppliers": new tables suppliers, purchase_orders, purchase_order_lines added in section 2 (supplier email/phone as personal contact data); retention (section 3) and shop/redact deletion scope (section 4) extended accordingly, including a note on structural cascade deletion. |
| 2026-06-07 | Pricing-model adjustment (Free + Basic): removed the obsolete "per-currency request" trigger ping and "Pro" tier references (sections 2.3 and 8.3). Form-of-address correction to the formal form in section 2.2. |
| 2026-05-29 | Clarification: merchants' Discord/Slack webhooks are not processing on behalf of Stockify but third-party recipients designated by the merchant (new section 8.2). Owner trigger pings to Discord clarified as PII-free internal communication (new section 8.3). Section 10 (third-country transfer) reduced accordingly. |
| 2026-05-27 | First version with full Art. 13 GDPR implementation: controller, legal bases, retention, cookies/sessionStorage note, Discord/Slack/Cloudflare as additional sub-processors, data subject rights, supervisory authority, third-country transfer with DPF + SCC |
This privacy notice is kept in sync with the code: changes to the data model or to the sub-processor stack trigger an immediate update of this file. The state of the most recent change is noted at the top under "Last updated".