GDPR opt-out
GDPR opt-out (an agent’s Governance group, ?tab=privacy) is the mechanism that lets an end user stop receiving messages, and lets an operator silence or re-enable a single contact.
It and the retention policy were briefly merged into one Data & privacy section, on the argument that a GDPR opt-out and a data TTL answer the same question. They were split back apart: one is a per-contact switch an operator flips daily, the other a destructive schedule nobody touches for months, and reading them as one form served neither.
Enforcement is deterministic. The agent is never asked to decide whether a contact is opted out - a gate at the top of the message handler and a check on every proactive send do it in code.
The contact list is read with agent.governance:read, and a contact’s status is changed with agent.governance:write. The configuration itself lives on the agent record and travels with it: reading it is agent:read, changing it agent:write.
Configuration
- Enable - master switch. Off (the default) means no gate runs and no contact is ever suppressed.
- Stop keywords - comma-separated. Default
STOP. A subscribed contact sending exactly one of these (case-insensitive, whitespace-trimmed, whole message) is recorded as opted out. - Resume keywords - comma-separated. Default
START. An opted-out contact sending one of these is re-subscribed. - Closing message - sent once on opt-out. Leave empty for a silent opt-out.
- Resume message - sent once on resume. Leave empty for a silent resume.
- Inject prompt hint - on by default. Adds an informational section to the system prompt so the agent can tell a user how to unsubscribe. The section explicitly instructs the agent not to process the opt-out itself.
The keyword match is exact, not substring: “STOP sending these” does not opt anyone out.
How the gate behaves
The gate runs before Room/task routing and before context preparation, so a stop keyword is always honoured first - even mid-burst.
| Contact status | Message | Outcome |
|---|---|---|
| Subscribed | a stop keyword | recorded as opted out, closing message sent (if set), turn ends |
| Subscribed | anything else | normal turn |
| Opted out | a resume keyword | re-subscribed, resume message sent (if set), turn ends |
| Opted out | anything else | silently dropped - no model call, no reply |
A resume keyword wins over a repeated stop keyword: an opted-out contact can always get back in.
Both transitions are persisted to the conversation (the user’s keyword plus the confirmation) so the exchange is visible in Conversations. Persisting is best-effort - a write failure never prevents the opt-out from being honoured.
What opt-out does not cover
- Synthetic channels. The
agent,scheduled, androomchannel types never participate - there is no real end-user contact behind them. - Automated task messages are exempt from the gate.
- Closing and resume confirmations are always delivered, even though the contact is opted out at that moment. Every other proactive outbound send to an opted-out contact is suppressed.
An opt-out status lookup that errors is treated as “not suppressed”, so an infrastructure fault never silently blocks legitimate traffic.
Contacts
Opt-out is per (agent, channel type, contact id). A contact with no stored row is subscribed; there is nothing to create for the normal case.
The contacts table lists channel, contact, date, and source - how the status was set:
user- the contact sent a stop or resume keyword.admin- an operator set it from this tab.agent- a tool call set it, on agents where the opt-out tool is enabled. Setting is idempotent: a contact already opted out is left untouched, so an earlier user-initiated opt-out is not overwritten.
From the table an operator can re-enable a contact, or opt one out manually by entering its channel type and contact id.
See also
- Retention - deleting conversation data after a period, the other half of the data-privacy story.
- Governance - content policy gates.
- Channels - where channel types and contact ids come from.