Overview
Spree ships the machinery an EU merchant needs to answer a data protection request, prove that consent was given, show a lawful sale price, and tell a buyer how long they have to change their mind. Core provides the data model, the services and the API. Management workflows built on top — a request queue, deadline tracking, automated retention runs — are left to your application or to Spree Enterprise.Subject requests
A person has the right to a copy of their data and the right to have it erased. Both arrive as aSpree::DataRequest, which records that the request
was made and how it was answered.
202 Accepted with a pending request. Building the export
reads a person’s whole history, so it happens in the background and the file
arrives by email as a signed link that expires. Asking again while a request
of the same kind is still in flight returns the one already running, rather
than starting a second.
An erasure request needs the account password, because erasure cannot be
undone and an unattended session should not be enough to trigger it.
Most requests never reach the storefront. They arrive as email to the
merchant, often from someone who can no longer sign in, so the same two
actions exist in the admin:
What erasure actually does
Erasure means anonymization.Spree::Customers::Anonymize replaces every
value that identifies a person — on the account, its address book, the address
snapshots on past orders, saved cards, connected identities, live sessions and
consent rows — and keeps everything that is a financial record.
Orders, payments, tax lines and line items are never touched. They carry their
own retention obligation under accounting and tax law, and deleting them would
trade one legal duty for another. On order addresses the country, state and a
truncated postal code survive deliberately, so the jurisdiction a past sale was
taxed in stays provable.
A customer.anonymized event is published when it completes.
Extending the export
Spree exports what Spree knows. If your application holds data of its own — a loyalty ledger, support history — add it through the hook:server/config/initializers/spree.rb
server/config/initializers/spree.rb
Consent
accepts_email_marketing says what is true now. Proving consent needs to know
when it became true and where the person agreed, so the customer also carries
email_marketing_consent_updated_at and email_marketing_consent_source, both
written automatically whenever the flag moves.
Acceptance itself is recorded as an event in Spree::ConsentRecord — at
registration, and at checkout for guests who have no account. Each row keeps
the purpose, the source, the moment, and a snapshot of the documents shown,
including a digest of the text. A merchant who later edits their terms can
still show which version a given person agreed to.
Consent rows survive erasure. The proof that consent was given outlives the
person’s contact details, which are removed from the row.
Cookie consent belongs to your storefront, not to Spree. These records cover
consent your backend can act on, such as marketing permission and accepted
terms.
Omnibus price history
The Omnibus Directive requires that an announced price reduction shows the lowest price of the previous 30 days. Spree records every base price change inSpree::PriceHistory and exposes the lowest prior price on request:
track_price_history, since a non-EU store has no use for it. Retention
defaults to 30 days via price_history_retention_days; prune with:
Right of withdrawal
EU buyers may withdraw from a distance sale within 14 days of receiving the goods. Orders expose the deadline:Response
withdrawal_period_days, because the right
is regional law. It is deliberately separate from return_window_days: a
return window is merchant policy running from purchase, often set to 30 days as
goodwill, while withdrawal is a statutory right running from delivery.
The deadline is computed on read from the order’s latest delivery, falling back
to completion while nothing has been delivered. A stored copy would be wrong
the moment a parcel arrived.

