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.
A customer opens a request from their account:
202 Accepted with the request awaiting_confirmation.
Nothing happens yet. A request also covers the guest orders placed at the
account’s email address, so it waits until it is confirmed from that address.
An erasure request also needs the account password, because erasure cannot be
undone and an unattended session should not be enough to trigger it.
Confirming a request
Spree publishesdata_request.confirmation_requested, carrying a confirmation
token. spree_emails sends the person a link to redirect_url with
?token= appended. A storefront that sends its own emails subscribes a webhook
endpoint to the event instead: it carries a live credential, so only an
endpoint that names it receives it (see Webhooks).
redirect_url is used only when it matches one of the store’s allowed origins.
Otherwise the link falls back to the store’s storefront URL.
The page the link opens confirms the request when the person presses a button:
Receiving the export
Building the export reads a person’s whole history, so it happens in the background. When it is ready,spree_emails sends a download link to the same
address. The link expires after seven days.
The download link is shown only to whoever holds the confirmation token, never
to the signed-in account. A storefront that sends its own emails reads it with
the token:
A store without
spree_emails, and without a webhook handler for
data_request.confirmation_requested, cannot complete a request a customer
raises. Requests staff raise in the admin still work.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 business-to-business order is the narrower case. An address book belonging to
a company is left alone entirely — it is shared by every member, and one
person’s erasure is no warrant to rewrite it. Where an order recorded delivery
to one of the company’s sites, the contact name and phone on that record are
erased and the site and trading name stay: they are the company’s details, not
the person’s, and taking them would erase the merchant’s record of a lawful
sale along with them.
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.

