Marketing consent
A customer’s marketing consent is kept per channel: email, sms (text
message), phone and post. Each is granted, withdrawn, never_asked,
other_address or unconfirmed. Every customer starts as never_asked; nothing is assumed.
Consent belongs to an address
Section titled “Consent belongs to an address”Every answer is stored with the email address it was given for. A yes counts
only while that is still the customer’s email: change the customer’s email,
and the old yes shows as other_address and drops off lists and exports
until the customer agrees again. For email the address must match exactly.
For the other channels, a yes given while the customer had no email stands
on its own.
unconfirmed is a tick from a guest checkout on the customer that order
made: nobody proved the address, so it never counts and is left out of
lists and exports. It is never turned into a yes for somebody else: when a
person later makes a shop account with that address, only their own tick
at sign-up counts. An unconfirmed tick lapses after 30 days and then shows
as never_asked; the history keeps it.
Merging and erasing
Section titled “Merging and erasing”Merging a duplicate into a customer brings only a no across: where the
duplicate withdrew and the kept customer has not (a yes, an unconfirmed tick,
or never asked), the kept customer is withdrawn too, written as office with the note
“Merged from …”. A yes never moves; the duplicate’s history stays with it.
Erasing a customer withdraws every channel. If the customer row is kept, the
history keeps what was agreed and when, but who and any note become Erased
and the address is cleared. If the customer is deleted, the history goes with
them. Erased customers are
never on a marketing list.
The office, the shop and the API read and write the same record, so a change anywhere shows everywhere straight away.
Reading
Section titled “Reading”GET /customers/{id}/marketingAuthorization: Bearer btl_…Needs customers:read. The answer has each channel as it stands, with when it
changed, the source and who, then the history newest first:
{ "channels": [ { "channel": "email", "state": "granted", "changedAt": "2026-10-04T09:12:00.000Z", "source": "shop_signup", "by": "Sam Shopper" }, { "channel": "sms", "state": "never_asked", "changedAt": null, "source": null, "by": null } ], "history": [ { "id": "0192…", "channel": "email", "state": "granted", "source": "shop_signup", "method": null, "by": "Sam Shopper", "byKind": "customer", "wording": "Send me news and offers from Northgate Gas by email. I can stop them at any time.", "note": null, "at": "2026-10-04T09:12:00.000Z" } ]}Sources are shop_signup, shop_checkout, shop_account, office, import
and api. wording is what the shopper saw beside the box, word for word.
GET /customers?marketing=email (and /customers/ids, and the
export) lists only customers who granted that
channel.
Changing
Section titled “Changing”PUT /customers/{id}/marketingAuthorization: Bearer btl_…Content-Type: application/json
{ "channel": "phone", "state": "granted", "method": "phone", "note": "Asked on the round" }Needs customers:write, which owners and admins have and drivers do not.
Recording agreement needs method: how the customer told you (phone,
in_person, in_writing, email or other). Withdrawing needs only the
channel and is always allowed, even while the business’s bill is unpaid.
A person signed in is recorded as source office; an API key or connected
app as api, under its own name. Setting a channel to the state it already
has changes nothing. An erased customer can be withdrawn but not granted
(409). Bottle staff in a support session are refused (403), because the
change would carry the name of the person they are looking as.
The shop
Section titled “The shop”- Sign-up has an unticked box, separate from the terms. A tick is held on
the shopper’s account until they confirm their email, then given to the
customers with that email as
shop_signup, with the wording and the time they ticked it. - Checkout has the same unticked box. Left unticked it changes nothing.
Ticked, it is given as
shop_checkoutonly from a signed-in shopper with a confirmed email, and only to their own customer: the one they are buying as, when they may speak for it (a household, or a trade customer they manage; not a buyer or accounts person), or a customer the order made from the email they typed, when that is their confirmed address. A guest’s tick on a customer the order made is kept asunconfirmedand lapses after 30 days. A guest whose email matches an existing customer gives that customer nothing. - The account page has Marketing preferences: a switch for each
channel, for each customer the account speaks for (a household, or a trade
customer it manages).
GETandPUT /storefront/sites/{host}/account/marketingneed a signed-in shopper, and thePUTmust come from the shop’s own page (itsOriginand a JSON body). Another customer is403.
The history
Section titled “The history”Consents recorded on the shop before this record existed (a confirmed shop
account’s yes, and ticks at signed-in checkouts) were carried over as
granted for email, with their original date, words and source. Guest
checkout ticks were not, as nobody proved the address.
Every consent change is recorded in the history. Existing entries cannot be edited through the API. Privacy erasure removes identifying information or deletes the history along with the customer, as described above. The history is included in the customer’s data download.
Assistants
Section titled “Assistants”The MCP tool get_marketing_consent reads a customer’s
consent and history. No tool changes it.