Effective date: 18 May 2026 — Version 1.7 (updated 13 November 2026)
Short version: Apart from the off-by-default advertising cookies described below, every HTTP cookie we set is strictly necessary: two keep you signed in, one remembers your language, and two exist only for a few minutes or hours while you save a free-builder draft or connect e-conomic. Your browser's localStorage and sessionStorage also hold your consent choice, small settings you chose yourself (such as light or dark theme), and a few short-lived notes that make a page work (such as the invoice you are typing in the free builder). The only tracking identifier is the PostHog visitor ID, stored in localStorage and only if you accept analytics. In the showroom, PostHog keeps its entries in the tab’s sessionStorage instead, and only if you allow the counting on the showroom’s start panel. Advertising measurement is a separate, off-by-default permission: nothing advertising-related is loaded or stored unless you switch it on, and we never use it to track you across other websites or to build advertising audiences. Every item is listed below, and an automated check runs on every code change to keep this list complete.
Last audited: 28 May 2026 — Production audit of pennapay.com and app.pennasystems.com via browser DevTools inspection (pre- and post-consent flows verified; cookies and localStorage inspected before consent, after Accept, and after Decline). Domain update, 3 October 2026: on 30 August 2026 our marketing site moved from pennapay.com to pennasystems.com, and pennapay.com now only redirects there. The items listed below as set on pennapay.com at the May audit are now set on pennasystems.com. A check of the live response headers on 3 October 2026 found that the language cookie is also set by our server when you open a Danish page; the table now says so. Completeness update, 13 November 2026: we compared this page with everything our code writes to your browser and found items that were missing. They are now listed: two more HTTP cookies (pp_draft_token, pp_economic_state), the PostHog opt-out flag, and the settings and notes kept in localStorage and sessionStorage (new sections 3 and 3a). We also corrected the PostHog description: until 13 November 2026 PostHog, once you had accepted analytics, also wrote its visitor ID to a one-year HTTP cookie as well as to localStorage. That contradicted this page, which said it sets no cookies. It now keeps the ID in localStorage only, and removes any old PostHog cookie it finds on your device. Showroom update, 13 November 2026: the showroom at pennasystems.com/showroom is now described on this page: the access_token cookie on showroom.pennasystems.com (two hours at the latest), the notes it keeps in your browser tab, and PostHog’s entries for the optional counting, which exist only if you allow it on the showroom’s start panel. Language cookie change, 4 October 2026: when our server sets the language cookie automatically on a Danish page, it is now a session cookie, and it is set only if your browser has no language cookie yet. The 1-year cookie is now set only when you choose a language yourself, with the language switch or in your settings. A language saved in your account settings now always takes precedence over the cookie in the app.
1. What is a cookie?
A cookie is a small text file stored on your device by your browser when you visit a website. Cookies help websites remember your preferences or keep you logged in. Under GDPR and the Danish cookie rules derived from the ePrivacy Directive (EU 2002/58/EC, as amended by 2009/136/EC), we must tell you which cookies we use and obtain your consent for any cookies that are not strictly necessary.
This policy also covers localStorage identifiers, which have the same privacy impact as cookies and are treated equivalently under current EDPB guidance.
2. Cookies we set
PennaSystems classifies cookies and similar client-side identifiers according to the four categories established under the ePrivacy Directive and EDPB guidance:
Strictly necessary — required for the platform to function (e.g. keeping you logged in, remembering your language). No consent required (ePrivacy Directive Art. 5(3) exemption for technically necessary storage).
Functional — improve usability without being essential (e.g. remembering UI preferences). Consent required where they are not strictly necessary.
Analytics — measure how the platform is used. Consent required for non-anonymous analytics or where the identifier persists across sessions.
Marketing / advertising — used for advertising purposes. Always require your consent, are declined by default, and can be withdrawn at any time. You can grant this category together with analytics using Accept all, or grant it on its own — or refuse it on its own — from the granular toggles under Manage preferences. We do not use this category to track you across other websites, to build advertising audiences, or to retarget you.
PennaSystems sets strictly necessary cookies (listed in the table below) and uses one consent-gated analytics identifier (the PostHog visitor ID, stored in browser localStorage rather than as a cookie — see §3 below; in the showroom, in the tab’s sessionStorage — see §3a). We do not set functional cookies: settings you choose yourself (such as the theme or the builder layout) and short-lived notes that make a page work are kept in localStorage and sessionStorage, not in cookies, and are listed in §3 and §3a. They are written only when you use the feature that needs them, they are not used to identify or track you, and the browser never sends them to our servers on its own, so we treat them as part of the service you asked for rather than as something that needs a separate consent. The only advertising cookies that can ever appear are the Google Ads conversion cookies described in §6, they appear only after you grant the advertising-measurement permission, and no other advertising or retargeting technology is used on this site.
The following table lists every HTTP cookie set by pennasystems.com, app.pennasystems.com and the showroom at showroom.pennasystems.com. pennapay.com only redirects to pennasystems.com and sets no cookies.
Name
Domain
Category
Purpose
Lifetime
HttpOnly
Set by
access_token
app.pennasystems.com, showroom.pennasystems.com
Strictly necessary
Stores your JSON Web Token (JWT) authentication credential. Required to keep you logged in. HttpOnly prevents JavaScript access; Secure flag ensures HTTPS-only transmission. Also set, for the account of the demo, when you open a demo link. After a two-step or passkey sign-in, our sign-in page also writes a one-day copy from JavaScript, but only if the server's copy is not already there, and that copy is not HttpOnly. In the showroom (showroom.pennasystems.com) it is set when the showroom opens, whichever button you pressed on its start panel, and it refers to the made-up studio, not to you.
30 days (or until logout); a demo ends it after at most 24 hours; in the showroom, 2 hours at the latest
Yes
Server
ps_session
.pennasystems.com
Strictly necessary
Session presence flag — value is always 1 and contains no personal data or auth credentials. Allows pennasystems.com to detect that you are logged in and update its button from "Sign in" to "Open dashboard". Set and cleared alongside access_token.
30 days (or until logout)
No
Server
penna-locale
.pennasystems.com
Strictly necessary
Stores your display language (en or da) and nothing else. Set when you choose a language with the language switch or save one in your settings, and by our server when you open a Danish page (an address starting with /da/) and your browser has no language cookie yet, so that pages and the app keep showing your language, including after you sign up on app.pennasystems.com. In the app, a language you have saved in your account settings always takes precedence over this cookie.
Session when set automatically on a Danish page; 1 year when you choose a language with the language switch or in your settings
No
Server (Danish pages, session only) and client (language switch, settings)
pp_draft_token
.pennasystems.com
Strictly necessary
A signed reference to the invoice draft you saved in the free invoice maker, so that it can be attached to your new account when you finish signing up. It holds a random draft number and no invoice content (the draft itself is stored on our server, as described in the Privacy Policy). Set only when you open the sign-up link from a saved draft, and deleted once the draft is attached to your account.
24 hours
Yes
Server
pp_economic_state
app.pennasystems.com (sent only to /api/bookkeeping/economic/callback)
Strictly necessary
A signed, single-use value that proves the e-conomic connection you approve is the one you started. It holds your organisation and account numbers and a random code, and protects you against a forged connection request. Set only when an organisation owner starts connecting e-conomic, and deleted when the connection finishes.
15 minutes (or until the connection finishes)
Yes
Server
_gcl_*, _gac_*
pennasystems.com, app.pennasystems.com
Advertising — consent required
Google Ads conversion measurement: records that a visit originated from one of our advertisements, so a later signup can be matched to it. Set by Google's conversion tag, and only after you switch on the Advertising measurement permission described in §6. If you have not granted that permission, the tag is never loaded and these cookies are never created. They contain no name or email address, and they are not used to build advertising audiences or to retarget you.
Up to 90 days (Google's default), or immediately deleted when you withdraw the permission
No
Google (conversion tag, consent-gated)
That is the complete list of HTTP cookies. Apart from the consent-gated Google Ads conversion cookies in the last row, we set no advertising cookies, no retargeting cookies and no cross-site tracking cookies of any kind — and that last row stays empty on your device unless you switch the permission on yourself.
3. localStorage items we store
In addition to HTTP cookies, we store the following items in your browser's localStorage. These are never transmitted to our servers by the browser, and they persist across browser sessions until you clear them. Items marked Functional are written only when you make the choice they remember, or close the hint they remember, and are not used to identify or track you. Where an entry says “(app)”, it exists only after you use that part of the app while signed in.
Key
Domain
Category
Purpose
Lifetime
Set by
cookie_consent_v2
pennasystems.com, app.pennasystems.com
Strictly necessary
Records your consent choices (analytics on/off, advertising measurement on/off, timestamp, schema version). Required to remember your decision and avoid showing the banner on every visit. The consent record itself is exempt from the consent requirement under the ePrivacy Directive.
Persistent (until you clear localStorage or the banner resets)
cookie-banner.js (set when you interact with the consent banner)
showroom_consent
pennasystems.com
Strictly necessary
On the showroom page (/showroom, /da/showroom): records which optional parts of the showroom you allowed (usage tips, counting what you open and click, technical data) and when, so the page does not ask again on your next visit. It is written only after you press Accept all, Reject all or Save and open the showroom, and it holds no name, e-mail address or other identifier. You can change it at any time with Privacy choices on that page.
Persistent (until you clear localStorage)
showroom-outer.js (set when you choose on the showroom page)
ph_{key}_posthog
pennasystems.com, app.pennasystems.com
Analytics — consent required
PostHog visitor state: a random device ID and session identifiers used to understand how the product is used. Contains no name, email, or other directly identifying information for anonymous visitors. Only stored after you accept analytics.
Persistent (removed immediately if you withdraw consent)
PostHog library (only initialized after you accept analytics)
PostHog's record that you opted out, so that it stays off. It holds an on/off flag and no visitor ID. It can exist only after you have withdrawn analytics consent while PostHog was loaded, and it is not removed when you withdraw, because removing it would switch PostHog back on. In the showroom (showroom.pennasystems.com) it is written when you press Stop counting in the showroom menu, so that the counting stays off in that browser.
Persistent (until you clear localStorage)
PostHog library
penna-theme
pennasystems.com, app.pennasystems.com
Functional
Your choice of light or dark theme (light or dark). Without it, the page follows your device's setting.
Persistent
theme-toggle.js (when you press the theme button)
penna-pricing-currency
pennasystems.com
Functional
The currency you picked on the pricing page, so the prices stay in it on your next visit.
Persistent
Pricing page (when you pick a currency)
penna-pricing-interval
pennasystems.com
Functional
Whether you chose monthly or annual prices on the pricing page.
Persistent
Pricing page (when you switch)
builder-form-panel-w:{path}
pennasystems.com, app.pennasystems.com
Functional
The width you gave the form panel in the invoice builder by dragging the divider. One entry per builder page, named after the page address.
Persistent (double-click the divider to reset it)
builder-splitter.js (when you drag the divider)
penna-order-time-panel-open
app.pennasystems.com
Functional
Whether you left the time panel open in the order editor (app).
Persistent
order-editor.js
pennaOrderFirstDismissed
app.pennasystems.com
Functional
That you closed the “start with an order” hint with Don’t show again (app). If you belong to several organisations, the key ends with a colon and the number of the organisation you were in.
Persistent
invoice-editor.js (when you tick “Don’t show again”)
pennapay_ob
app.pennasystems.com
Functional
That you dismissed the getting-started checklist on the dashboard (app).
Persistent
dashboard.html (when you dismiss it)
pp_dismissed_wf
app.pennasystems.com
Functional
The record numbers of the follow-up suggestions you dismissed on the dashboard (app). The numbers mean nothing outside your account.
Persistent
dashboard.html (when you dismiss a suggestion)
pennapay_clock_state
app.pennasystems.com
Functional
Your running time-tracker, so the timer survives a reload or a closed tab: start time, pause state, the description you typed and the client you picked (app).
Until you stop or save the timer
time.html (when you start the timer)
pennapay_bill_currency
app.pennasystems.com
Functional
The currency you last used when turning tracked time into an invoice line (app).
Persistent
time.html (when you bill tracked time)
penna-demo
app.pennasystems.com
Strictly necessary
A note that this browser opened a demo and when that demo ends, so that after it has ended you are told the demo is over instead of being sent to a log-in page for an account you never had. It holds an end time and nothing else.
Until 24 hours after the demo ends
demo-entry.js (when you open a demo link)
If you decline analytics, no PostHog entry is created. The consent record and the Functional and Strictly necessary items above can still exist, because they remember your own choices.
3a. sessionStorage items we store
sessionStorage works like localStorage but is emptied by the browser when you close the tab. None of it is transmitted to our servers by the browser.
Key
Domain
Category
Purpose
Lifetime
Set by
pp_create_draft
pennasystems.com
Functional
The invoice you are typing in the free invoice maker, so a reload of the page does not lose it. It stays in your browser unless you choose to save the draft with us.
Until you close the tab
create.js (as you type)
penna-origin
pennasystems.com
Functional
The address and title of the invoice-template page you came from, so the home page can offer a “back to” link. The address is always the page’s own, never taken from a link you followed.
Until you close the tab
origin-bridge.js (on invoice-template pages)
penna_ct_{token}
pennasystems.com
Strictly necessary
On a client conversation page, the access code you received when you confirmed your email address, so a reload does not ask again. The part after penna_ct_ is the code in the link you were sent.
Until you close the tab
c.html (after you confirm your email)
penna_ce_{token}
pennasystems.com
Strictly necessary
On the same page, the email address you confirmed, used to pre-fill the booking sheet.
Until you close the tab
c.html (after you confirm your email)
portal_msg_email
pennasystems.com
Functional
On a client invoice page, the email address you typed to message the sender, so you do not retype it for each message.
Until you close the tab
portal.html (when you send a message)
penna-showroom-return
showroom.pennasystems.com
Strictly necessary
In the showroom: the address of the screen you were on when you chose See it as your client, so that Back to your view takes you there again.
Until you close the tab
showroom.js
penna-showroom-active
showroom.pennasystems.com
Strictly necessary
In the showroom: a note that this tab has already had a working showroom visit, so that if the visit runs out, the showroom starts a new one and tells you it was refreshed. It holds the value 1.
Until you close the tab
showroom.js
penna-showroom-restart
showroom.pennasystems.com
Strictly necessary
In the showroom: when this tab last started a new visit by itself, so that it never does so twice within 20 seconds. It holds a time.
Until you close the tab
showroom.js
penna-showroom-refreshed
showroom.pennasystems.com
Strictly necessary
In the showroom: tells the next screen to show the note that the showroom was refreshed. Removed as soon as the note has been shown.
Until the note is shown, or you close the tab
showroom.js
penna-showroom-tips-off
showroom.pennasystems.com
Functional
In the showroom: that you switched Show me usage tips off on the start panel, so that the tips stay off on every screen of this visit. Written only if you switched them off.
Until you close the tab
showroom-tips-data.js (when you switch the tips off)
penna-showroom-insight
showroom.pennasystems.com
Analytics — consent required
In the showroom: remembers, for this tab only, that you allowed the optional counting and when this visit began, so that every screen follows your choice. Written only if you allowed counting, and removed when you press Stop counting.
Until you close the tab, or at once when you press Stop counting
showroom-insight.js (only after you allow counting)
ph_{key}_*
showroom.pennasystems.com
Analytics — consent required
PostHog’s entries for the optional counting in the showroom (names beginning ph_, such as ph_{key}_posthog and ph_{key}_window_id): a random visitor ID and session ID, and which browser window they belong to. They hold no name, e-mail address or IP address. Written only if you allowed counting on the showroom’s start panel (Accept all, or Save and open the showroom with Let us see what I click or Accept technical data still ticked).
Until you close the tab, or at once when you press Stop counting
PostHog library (showroom, only after you allow counting)
4. Analytics tools we use
PostHog Analytics
We use PostHog (PostHog, Inc., EU Cloud) for product and web analytics. PostHog:
Stores a random visitor ID in your browser's localStorage (the ph_{key}_posthog entry above). It does not set HTTP cookies.
Does not collect or store IP addresses in identifiable form.
Records: page URL, referrer domain, country (country-level only), browser family, device type, and in-app feature usage events.
For logged-in users, events are linked to your PennaSystems account ID so we can understand how features are used.
All data is stored on EU servers (AWS eu-central-1, Frankfurt). No data leaves the EU.
No advertising profiles are created. No data is sold or shared with third parties for advertising.
PostHog is only initialized after you grant analytics consent. Until 13 November 2026 it also wrote its visitor ID to a one-year HTTP cookie (ph_…_posthog); that cookie has been switched off, and any copy still on your device is deleted the next time you visit. You can clear the PostHog visitor ID at any time by withdrawing consent below or by clearing localStorage for our domains in your browser's developer tools (Application → Local Storage → delete the ph_* key). PostHog privacy policy →
In the showroom (showroom.pennasystems.com), PostHog runs only if you allow it on the showroom’s own start panel, not through the cookie banner: after Accept all, or after Save and open the showroom with Let us see what I click or Accept technical data still ticked. The boxes start ticked as a suggestion (on a later visit, they show your saved choice), nothing is counted until you press a button, and Reject all switches the counting off. There PostHog keeps its entries in the tab’s sessionStorage, not in localStorage or a cookie, does not look up your location, removes query strings from page addresses, records no video of your screen and never links the counts to an account. You can stop it at any time with Stop counting in the showroom menu. What is counted, and for how long it is kept, is described under Showroom in our Privacy Policy.
When you initiate a paid subscription, Stripe's checkout page may set Stripe-specific cookies on its own domain. These are governed by Stripe's cookie policy, not ours. We have no control over them once you are on Stripe's checkout page.
6. Third-party cookies and advertising
Apart from the consent-gated Google Ads conversion tag described immediately below, we embed no third-party advertising scripts, social media widgets, or tracking pixels. No other third-party HTTP cookie is set by any content originating on our pages.
If you follow a link from our site to a third party (e.g. Stripe's payment page), that third party may set its own cookies governed by its own cookie policy.
Google Ads conversion measurement
We use a single Google Ads conversion tag, and only to answer one question: did someone who clicked one of our advertisements go on to create an account. It is the only advertising technology on this site.
It is governed by two independent switches, and it runs only when both are on. The first is yours: the Advertising measurement permission, described in §7. The second is ours: a server setting that determines whether an advertising account is configured at all. When that server setting is off, no Google host is permitted by our Content Security Policy and no request reaches Google from any visitor, whatever they have consented to. Turning it on is a material change to this policy: we announce it in our legal changelog at least 14 days before it takes effect (§10), so that changelog — not this paragraph — is the authoritative record of whether we are currently advertising.
This is what does and does not happen, in both states:
Without your permission, nothing is requested from Google at all. We use Google's basic consent mode, which means the tag is not loaded before consent — not even in the cookieless "consent signal" form that Google's advanced mode permits. Refusing costs you nothing and hides nothing: every part of this site works identically either way.
The permission is declined by default. It is granted only if you choose Accept all on the cookie banner, which names advertising measurement as one of the things it turns on, or if you switch that single category on under Manage preferences. Reject all leaves it off, and so does ignoring the banner.
With the permission on, Google Ads sets its own cookies (names beginning _gcl_ and _gac_) so that a signup can be matched to the advertisement that led to it. Google acts as an independent controller for the data it receives through that tag; its use of it is governed by Google's own privacy terms, not by ours.
Ad personalisation and remarketing stay switched off even after you consent. We measure whether an advertisement worked; we do not build advertising audiences and we do not retarget you elsewhere. This is enforced in our own code, not left to a setting in an advertising console.
We do not use Enhanced Conversions and do not send your email address, hashed or otherwise, to Google.
Switching the permission back off deletes those cookies immediately and instructs Google to stop using advertising storage. This runs on every page load while the permission is off, so withdrawal is enforced and not merely recorded.
If we ever widen what this tag does, we will update this policy first, on the notice period described in section 10.
7. Your consent
The strictly necessary items listed in Sections 2, 3 and 3a (access_token, ps_session, penna-locale, pp_draft_token, pp_economic_state, cookie_consent_v2, penna-demo, showroom_consent, the showroom’s tab notes (penna-showroom-return, penna-showroom-active, penna-showroom-restart, penna-showroom-refreshed) and the client-page keys penna_ct_… and penna_ce_…) do not require your consent under GDPR Art. 6(1)(b) (performance of a contract) and the ePrivacy Directive exemption for technically necessary cookies.
The Functional items in Sections 3 and 3a are settings and hints you set yourself. We do not ask for a separate consent for them: they are written only when you use the feature, they are not used to identify or track you, and the browser never sends them to us on its own. You can delete them at any time (§8).
Cloudflare Web Analytics sets no cookies and creates no client-side identifiers; it operates without consent under the ePrivacy strictly-necessary exemption.
PostHog stores a random visitor ID in localStorage. PostHog is only initialized after you grant analytics consent on the cookie banner.
The showroom has its own start panel instead of this banner; it is described in §4 and under Showroom in our Privacy Policy. The banner choices below do not switch the showroom’s counting on or off.
The banner offers three choices, and every optional category is declined until you choose — closing or ignoring the banner grants nothing:
Accept all — grants both optional categories: analytics and advertising measurement. The banner names both before you press it.
Reject all — grants neither. It is presented with the same size, weight and prominence as Accept all; refusing is exactly as easy as accepting, and costs you no functionality.
Manage preferences — a separate on/off control for each category, so you can grant analytics without advertising, or advertising without analytics. Nothing is pre-ticked.
You may change or withdraw any of these choices at any time, without giving a reason and without affecting the lawfulness of what happened before you withdrew. Withdrawing the advertising permission also deletes the associated cookies (§6). Use the button below, or the link in the site footer:
8. How to control or delete cookies
You can control and delete cookies at any time through your browser settings:
Chrome: Settings → Privacy and security → Cookies and other site data
Firefox: Settings → Privacy & Security → Cookies and Site Data
Safari: Preferences → Privacy → Manage Website Data
Edge: Settings → Cookies and site permissions → Cookies and site data
Impact of deleting specific items:
Deleting access_token will log you out of PennaSystems.
Deleting ps_session will hide the "Open dashboard" shortcut on pennasystems.com until your next login.
Deleting penna-locale will reset your language to your browser's default on next visit. In the app, a language saved in your account settings still applies.
Deleting cookie_consent_v2 from localStorage will cause the consent banner to reappear on your next visit.
Deleting ph_* keys from localStorage removes your PostHog visitor ID.
Deleting showroom_consent makes the showroom page forget your choice. The showroom’s sessionStorage entries disappear when you close the tab, and Stop counting in the showroom menu removes PostHog’s at once.
Deleting a Functional item (for example penna-theme or penna-pricing-currency) resets that one setting or brings the hint back. Deleting pennapay_clock_state discards a running timer. Deleting penna-demo only changes where you land after a demo has ended. sessionStorage is emptied when you close the tab.
Deleting pp_draft_token means a saved free-builder draft is no longer attached to your new account; you can still start from the draft link again within its lifetime. Deleting pp_economic_state makes an e-conomic connection in progress fail safely; just start it again.
Deleting _gcl_* or _gac_* cookies removes Google Ads' record of which advertisement led you here. They exist on your device only if you granted the advertising-measurement permission; withdrawing it deletes them for you.
9. Do Not Track
We do not track you across other websites, build advertising profiles, or share your browsing with data brokers — with or without a Do Not Track signal, and whatever you have consented to here. Our practices therefore already align with what DNT requests, and we do not alter our behaviour in response to the signal. The advertising-measurement permission in §6 does not change this: it measures whether one of our own advertisements led to a signup on our own site, and it is off unless you switch it on.
10. Changes to this policy
If we add new cookies or materially change how we use existing ones, we will update this policy and display a notice on the site at least 14 days before the change takes effect. Registered users will also be notified by email if the change affects the authenticated application.
11. Contact
Questions about our use of cookies or this policy:
PennaSystems (CVR 46522036) privacy@pennasystems.com
We respond within 5 business days.
You may also contact Datatilsynet (the Danish Data Protection Agency) at dt@datatilsynet.dk if you wish to lodge a complaint about how we use cookies or process personal data, or you may contact the supervisory authority in your EU/EEA country of residence.