Privacy policy
How Puffin Analytics handles data from the accounts you connect — what it keeps, what it discards at the door, and who else ever touches it.
Effective [[EFFECTIVE_DATE]]. Last updated [[EFFECTIVE_DATE]].
Draft pending review. This document has not been reviewed by a lawyer and contains unfilled placeholders such as [[ENTITY_ADDRESS]]. Remove this notice once the placeholders are filled and the text has been reviewed.
The short version
Puffin Analytics exists to tell a merchant whether an order made money. That question never needs a shopper’s identity, so the product is built not to hold one.
- Shopper names, email addresses, phone numbers, postal addresses, dates of birth, national identifiers and payment card details are discarded before anything is written to disk, and are not stored anywhere in the system.
- A shopper is represented by a single identifier — your platform’s own customer id, or for a guest checkout a keyed one-way hash of the email that cannot be reversed and does not match the same person across two stores.
- We read from your connected accounts. The only thing the product can write anywhere is the membership of a Meta product set, and only when you set up a rule asking for it.
- We do not sell data, do not use it for advertising, and do not combine one customer’s data with another’s.
- The product itself carries no analytics, tracking or advertising scripts.
The rest of this page is the detail behind each of those sentences, including where the guarantees are absolute and where they depend on how the product is configured.
Who we are and what this covers
Puffin Analytics is operated by [[ENTITY_LEGAL_NAME]] (“Puffin One”,
“we”), [[ENTITY_ADDRESS]]. This policy covers the Puffin Analytics
application at
analytics.puffinone.com, its API, and this website.
We handle two different kinds of data, in two different roles:
- Data about your business and your shoppers, which arrives from the accounts you connect. You decide what is connected and why; we process it on your instructions to run the service. In European data-protection terms you are the controller and we are the processor.
- Data about the people who use Puffin Analytics — you and your colleagues. We decide how those accounts work, so for that data we are the controller.
We are not a consumer product. Shoppers do not have accounts with us, cannot sign in, and are not shown anything by us. If you are a shopper who has bought from a store that uses Puffin Analytics, see section 13.
Where the data comes from, and what access we ask for
Nothing is imported until you connect a source. Each connection is authorised by you, through that provider’s own consent screen or with a credential you supply, and can be revoked by you at any time from within the product or at the provider.
These are the exact permissions requested:
| Source | Permission requested | What we do with it |
|---|---|---|
| Shopify |
read_products, read_orders,
read_all_orders, read_inventory,
read_locations, read_discounts,
read_customers
|
Read orders, refunds, products, variants, inventory levels and discounts. Every scope is a read scope; the app issues no write operations to Shopify. |
| Magento | An access token you create in your own Magento admin |
Read orders, credit memos, products, stock and product attributes over the REST
API. The client issues GET requests only.
|
| Google Ads | https://www.googleapis.com/auth/adwords |
Run reporting queries for campaign, ad group, ad and asset spend and performance. Google publishes no read-only variant of this scope; we use it solely to read reports and never to create or change a campaign, budget, ad or asset. |
| Google Analytics 4 | https://www.googleapis.com/auth/analytics.readonly |
Read daily aggregate reports: product views, add-to-cart counts, collection impressions and session counts. |
| Google Sheets | https://www.googleapis.com/auth/spreadsheets.readonly |
Read the value range of the specific sheets you nominate, for figures such as product cost, shipping rates and calendar events. |
| Meta |
ads_read, business_management,
catalog_management
|
Read ad account spend, performance and creatives; list your businesses and
product catalogues. catalog_management additionally permits the one
write described below.
|
We request no Google identity, profile, email or Drive scope. Puffin Analytics does not offer “sign in with Google”, and we never learn the Google account behind a connection.
The one write
If you create an activation rule, the product can set a Meta product set’s name and the list of product identifiers it contains, so a catalogue campaign targets the products your report selected. That is the entire write surface. It never touches campaigns, ad sets, ads, budgets or creatives, and it only runs for a rule you created on a connection that was granted catalogue permission.
A note on publicly shared sheets
A Google Sheet can also be supplied as a public share link rather than a connected account. In that case the product fetches it over the public export URL with no credential. That sheet is readable by anyone holding the link, us included. Connect the sheet through Google instead if that is not what you want.
Google user data and the Limited Use requirements
Puffin Analytics’ use and transfer to any other app of information received from Google APIs will adhere to the Google API Services User Data Policy, including the Limited Use requirements.
Specifically, in respect of data obtained through Google APIs:
- What we obtain. From Google Analytics 4, daily aggregate report rows: product view counts, add-to-cart counts, item-list impressions and clicks, collection page views and session counts, each by date and by product or collection. From Google Ads, campaign, ad group, ad and asset level cost, impressions, clicks and conversions by date, plus ad text, headlines, landing page URLs and creative images. From Google Sheets, the cell values of the specific sheets you nominate.
- Why. Solely to provide and improve the user-facing features you connected the account for: attributing advertising cost to products, reporting return on spend and contribution margin, valuing inventory, and resolving the costs you keep in a spreadsheet.
- We do not transfer it to others, except to the sub-processors listed in section 11 who host or transmit it on our behalf, where you direct us to, or where the law requires it.
- We do not use it for advertising of any kind, and we do not sell it.
- We do not use it to develop, improve or train generalised artificial-intelligence or machine-learning models.
- No human reads it, except where you give explicit consent for a specific piece of data, where it is necessary for security purposes or to comply with the law, or where the data has been aggregated and anonymised for internal operations. In practice the only routine human access is by our own engineers investigating a fault you have reported.
Data received from Google APIs is stored in the same database as the rest of your data and is subject to everything in section 10. Access tokens and refresh tokens issued by Google are encrypted at rest. Disconnecting the source, or revoking access in your Google account’s security settings, stops all further access immediately.
Your shoppers: what we discard and what we keep
This is the part of the product we have worked hardest on, so it is worth being precise about what is a guarantee and what is a configuration choice.
Discarded at the door
These never enter the system. They are not requested where a platform lets us choose fields, and where it does not, they are removed at the moment data lands, before anything is stored:
- Shopper and recipient names
- Email addresses
- Phone and fax numbers
- Street addresses, postcodes and cities
- Dates of birth
- National and government identifiers, and tax identifiers
- Payment card details, including cardholder name and the card number’s leading digits
- Shopper IP addresses
- Order comment and status-history threads
The two platforms need different techniques for this. Shopify’s API lets us name the fields we want, so the order query asks only for what the product uses — for an address, the country code and nothing else. Magento returns a whole order object with no field selector, so every Magento order and credit memo is passed through an allow-list: a fixed set of fields is copied out and everything else is dropped, including any field that did not exist when the list was written. It is a list of what to keep, not a list of what to remove, so a new personal field appearing in a future Magento version is discarded by default rather than kept by oversight.
Kept
- One identifier per shopper. For a registered customer, your platform’s own customer id. For a guest checkout, a keyed hash of the email address: HMAC-SHA256 under a secret held only by our server, with your store id mixed into the input. It cannot be reversed to an email, and the same person shopping at two stores produces two unrelated values, so nothing can be correlated across merchants.
- The destination country of an order. Not the address.
- Whether the shopper was new or returning, and their order count, first and last order dates, and lifetime totals — all keyed to that one identifier.
- The order itself: line items, quantities, prices, discounts, taxes, shipping, costs, currency, payment gateway name, delivery method and dates.
We describe this as pseudonymous, not anonymous. A platform customer id can be looked up in your own store admin, so under laws such as the GDPR this data remains personal data and is treated as such. We do not claim otherwise.
The one place free text can enter
Checkouts often collect custom fields — a ring size, an engraving, a gift message. An administrator can promote one of these fields so that it becomes reportable. Two things follow, and both matter:
- A field that has not been promoted never lands. This is enforced structurally: every custom-attribute container is stripped out of the incoming record at every level of nesting, unconditionally, and only explicitly promoted keys are then written back. There is no path into storage that skips this step.
- The values of a promoted field are stored as they arrive. The product does not inspect them. If an administrator promotes a field that shoppers use to type personal information, that information will be stored. Choosing which field to promote is the whole of the decision, and it is a decision made by your administrators, not by us.
We tried automatic detection of personal data inside such values and removed it, because it was not good enough to rely on: a tuned detector could not reliably tell a list of clothing sizes from a phone number, and a control that fails silently is worse than none. We would rather state the limit plainly than imply a protection that does not hold.
Your business data
Alongside order facts, the system stores:
- Products and variants — titles, handles, SKUs, barcodes, option values, categories, brands, tags, collection membership, prices and image URLs.
- Costs you supply — per-SKU cost history with effective dates, delivery and payment method rate tables, tax rates, and any notes you attach to them.
- Inventory — a daily snapshot of quantity on hand, available and incoming per item per location, with monthly and daily rollups, and the cost value attached to each.
- Advertising — daily spend, impressions, clicks and conversions per campaign, ad set and individual ad, plus creative metadata: ad names, headlines, body copy, landing page URLs and preview links. Creative images are downloaded and stored so that they remain viewable after a platform expires their URLs.
- Traffic — daily counts of product views, add-to-carts, list impressions and sessions. These arrive from Google Analytics 4 as aggregates: the product holds no per-visitor or per-session record, no visitor id and no clickstream.
- Raw source records. The original responses from each platform are retained after the filtering described in section 5, so that reports can be rebuilt without re-downloading years of history.
- Your own work — saved reports and scenarios, dashboards, widgets, labels, goals, calendar events, exclusion rules and their names and descriptions, plus the query you ran and its result, cached so a repeated report is fast.
You and your team
For each person with an account we hold:
- name, email address, an Argon2 hash of the password (never the password), whether the account is active, when it last signed in, and display preferences such as language;
- the roles granted and the organisations and stores they apply to;
- an audit log of administrative and security-relevant actions, recording who did what, when, and the IP address and browser user agent the request came from. Values that look like a password, token or secret are removed from audit records before they are written;
- sign-in security records: hashes of active refresh tokens, password reset tokens and email one-time codes, with the IP address that requested a reset or a code.
We send email for a small number of transactional purposes only: a one-time sign-in code, a password reset link, a temporary password issued by an administrator, and an account-ready invitation. There is no marketing email and no newsletter. A one-time sign-in code appears in the subject line so it is readable from a lock screen — treat it as you would any other code sent by email.
Cookies and tracking
The Puffin Analytics application uses no cookies for authentication and carries no analytics, advertising, session-recording or error-reporting scripts. There is no Google Analytics tag, no advertising pixel and no third-party script of any kind in the application page.
Your sign-in session is held in your browser’s local storage on the device you use, along with your language and a few interface preferences. Signing out clears it. Because it lives in local storage rather than a cookie, it is not transmitted to any other site.
This marketing site carries no scripts and sets no cookies either. Our network provider, Cloudflare, may set a cookie for security and bot-protection purposes as traffic passes through it.
AI features
The product includes two optional features that can use a large language model: a weekly written summary of what changed in your numbers, and a helper that turns a question in plain language into a report.
In the current production deployment these are off. No model provider key is configured, so no request is made to any model provider and the weekly summary is generated from deterministic rules alone.
If they are ever enabled for your store, this is what would be sent to the model provider (which may be Anthropic, OpenAI or Google, depending on configuration):
-
For the weekly summary: aggregate figures only — your store’s currency,
the week in question, a table of weekly totals, and the candidate observations the
rules produced. The code refuses to send the payload at all if any field name in it
looks like personal data (
customer,email,address,phone, a name, or a postcode), and fails rather than sending it. - For the report helper: the question you typed, the list of available report fields, and a sample of distinct values from your own dimensions, which may include product titles, categories and brands.
Order-level data, shopper identifiers and promoted attribute values are not part of either payload. We would not enable these features for a store without telling you first, and not before confirming that the provider does not use data sent through its API to train its models.
Separately, we do not use your data to train any model of our own, and nothing described anywhere in this policy is used for that purpose.
How the data is protected
- Credentials are encrypted at rest. Every integration secret — OAuth access and refresh tokens, API keys, the Magento token — is encrypted with AES-256-GCM under a key held only in the server’s environment, with a fresh random initialisation vector per value and an authentication tag stored alongside. The database holds the ciphertext; a database copy without that key yields nothing. If the key is absent the service refuses to start rather than falling back to storing anything in the clear.
- Passwords are hashed with Argon2 and are never stored or logged in any recoverable form. Minimum length is ten characters.
- Sessions use rotating, hash-only refresh tokens. Only a SHA-256 hash of a refresh token is stored, never the token. Every refresh issues a new token and revokes the old one, and presenting an already-used token revokes that whole chain of sessions as a suspected theft.
- Access is scoped and enforced server-side. Four roles — super admin, organisation admin, store admin and member — determine what each person can see, and every query is scoped to a store the requester has been granted.
- No inbound ports are open. The server accepts no direct connections from the internet. All traffic arrives over an outbound tunnel to Cloudflare, which terminates TLS. The database and job queue are reachable only from the application itself.
- Secrets are kept out of logs. Error text and logs are passed through a redactor that strips platform access tokens, bearer headers, API keys and JWTs before anything is written.
No system is perfectly secure, and we do not claim a certification we do not hold. If you find a security problem, please report it to [[SECURITY_EMAIL]].
[[BREACH_NOTIFICATION_TERMS]]
Where the data lives and who else handles it
The service runs on a single dedicated virtual server rented from Hetzner Cloud, located in [[HOSTING_REGION]]. The application, the PostgreSQL database and the job queue all run on that one machine. We do not use a public cloud data platform, and your data is not replicated to one.
These are the third parties that can hold or transmit data on our behalf:
| Provider | What it handles |
|---|---|
| Hetzner Cloud | The server the application and database run on, and the disk they are stored on. |
| Cloudflare | All web traffic to and from the application passes through Cloudflare, which terminates TLS and provides the tunnel that is the only route in. Cloudflare R2 also stores downloaded advertising creative images. |
| Resend | Delivery of the transactional emails listed in section 7. Recipient address and message content pass through it. |
| Frankfurter | Daily currency reference rates. We send only a currency pair and a date range; no customer or order data. |
| [[ANY_ADDITIONAL_SUBPROCESSORS]] | — |
The platforms you connect — Shopify, Magento, Google and Meta — are sources rather than sub-processors: we read from them, and apart from the single Meta product-set write we do not send data to them. Their own privacy policies govern what they hold.
Backups. The database is dumped nightly and kept on the same machine under a fourteen-daily, eight-weekly, six-monthly rotation, so a copy of the database may exist for up to roughly six months. Configuration secrets in a backup are encrypted; the database dump itself is not, and is protected by the security of the host. A second copy held off the machine is planned and is not yet in place — we would rather say so than imply a resilience we do not have.
How long data is kept
Analytics is a historical discipline: a three-year cohort curve needs three years of orders. Data imported from your sources is therefore kept for as long as your account is active, so that historical reports keep working. There is no automatic expiry of order, product, inventory, advertising or traffic data. [[RETENTION_POLICY]]
Two exceptions run on their own schedule:
- Cached report results expire automatically a short time after the report was run.
- Password reset tokens and email sign-in codes expire within minutes and are single-use.
When your account is closed we delete your data as described in the terms of service. Backup copies then age out on the rotation described above.
You can shorten any of this at any time by asking us to delete a store, a source’s imported history, or a specific person’s identity link.
Your rights, and erasing a person
If you use Puffin Analytics
You can ask us for a copy of the personal data we hold about your account, to correct it, or to delete it. Reports and their data can be exported from within the product at any time as CSV or Excel. Write to [[PRIVACY_EMAIL]].
If you are a shopper
We have no relationship with you and cannot identify you from our own records. Please contact the store you bought from. They are the controller of that data, and they can instruct us.
How an erasure actually works
When a merchant asks us to erase a person, we do not delete their orders — deleting an order would silently change the merchant’s financial history. What is removed is the link between those orders and a human being. In one operation:
- the shopper identifier is cleared from every order line, from the order lifecycle record and from the raw stored source records;
- any promoted custom-field values on that person’s orders are emptied;
- the customer row itself — order counts, lifetime totals, cohort membership — is deleted outright.
The money stays and the totals do not move; the orders become unattributable. The record we keep that the erasure happened stores only a short one-way fingerprint of the key, never the email address and never the identifier itself, so that the act of erasing does not itself create a new copy of what was erased.
Erasure is carried out by us on request rather than automatically, and it can be requested by email address or by platform customer id.
Complaints
If you believe we have handled personal data improperly, please tell us first at [[PRIVACY_EMAIL]]. You also have the right to complain to a data protection authority. [[SUPERVISORY_AUTHORITY]]
[[DPO_DETAILS]]
International transfers
Your data is stored on a server in [[HOSTING_REGION]]. The providers listed in section 11 operate globally, so data passing through them may be processed outside that country. [[TRANSFER_MECHANISM]]
Children
Puffin Analytics is a business tool and is not directed at children. We do not knowingly create accounts for anyone under 16. Because shopper identities are discarded at ingestion, the product does not hold data that would identify a child who bought from one of your stores.
Changes to this policy
We update this policy when the product changes. The date at the top shows when it last changed. If a change materially affects how we handle your data, we will notify account holders by email before it takes effect.
Contact
[[ENTITY_LEGAL_NAME]]
[[ENTITY_ADDRESS]]
Privacy: [[PRIVACY_EMAIL]]
Security: [[SECURITY_EMAIL]]
General: [[SUPPORT_EMAIL]]