Security implementation notes
Last updated: 8 October 2026
Detail behind the Security & Data Handling page, for reviewers who want the mechanics. Nothing here changes what that page says.
Token format
A pseudonymiser match is replaced with a deterministic token of the shape [[email_kfbmocja]]: a type prefix and a hash keyed to your workspace, written as eight letters from a to p (more, in the rare case two values would collide), the same token every time for the same value, so the conversation still reads coherently and the model can refer to "the customer" consistently. In the knowledge base the customer's email address becomes a stand-in of the shape jane.doe-4f2a@anon.local, again stable per customer, so indexed records still group by person without holding the real address.
Why four weak identifiers are left intact
These are left intact both on the way to the model and in the knowledge base, because each pattern collides with ordinary support text and redacting it would corrupt the ticket. Over-redaction is not free at rest either: a stored article's text is copied into the drafting prompt as the citation the reply is built from, and these tokens are one-way, so a version number lost to an IP pattern cannot be recovered from the stored row.
| Left intact | Why |
|---|---|
| URLs | The pattern that matches a URL also matches wp-config.php and v2.5.1. Stripping the customer's links guts the draft. |
| IP addresses | The IPv4 pattern matches version numbers (Astra 4.6.3.1); the IPv6 pattern matches timestamps (10:15:30). |
| Postcodes / ZIPs | A bare five-digit pattern eats ticket numbers: #12345 becomes a token. |
| Street addresses with no lead-in | The street-suffix list contains ordinary English words (Way, Row, Path) and surname particles (van, del, von), so it corrupts prose, including the customer names this feature exists to protect. |
Links used to be the exception: one that carried a path or a query was tokenised in the knowledge base, on the reasoning that a reset link is worth sealing in a record we keep. That rule was withdrawn in August 2026 and the four above are now treated identically on both paths. Two things settled it. It sealed nothing the live path did not already hand over, because the same link in the same ticket always reached the model intact. And it cost every link in the knowledge base: the pattern admits anything carrying a /, which is every documentation link ever written, and these tokens are one-way, so a stored article could no longer link to the page it was quoting.
What the withdrawal gives up is narrower than the rule it removed, and worth stating exactly. A credential inside a link is still replaced whenever the API-key and licence-key patterns can claim it, and those need one long contiguous run: a 20-plus character token, a UUID, a signed-URL signature. They do not claim a short link id, a token broken up by dashes or dots (URL-safe base64, and every JWT), or a short value in a query string whose parameter is not named like a credential. Those are now retained with the link. A query parameter that does name itself — token, key, secret, code, licence and the like — is claimed by the licence-key pattern when its value mixes letters and digits, so ?token=aB3xY9kL is replaced while ?t=aB3xY9kL and ?code=EXPIRED are not.
That residual is accepted rather than overlooked, for three reasons. The same link already reached the model in the clear on the live path and always has, so the stored copy is not the more exposed of the two. The stored copy is scoped to your workspace and is covered by the retention schedule below, so it is neither shared nor kept indefinitely. And the link came from your own customer, in your own helpdesk, which you can read regardless — what changes is how long we keep it, not who can see it. The practical consequence, said plainly: treat a reset or invite link pasted into a ticket as retained.
The account block, and why it is not redacted
When a workspace connects WooCommerce, Shopify, Stripe or Freemius, Overdesk looks the customer up by the address they wrote in from and puts what comes back into the prompt as a short block. That block runs through the same pseudonymiser as everything else and is written so that nothing in it matches: an order number is not card-shaped, a plan name is not an identifier, and a licence key is already cut to its last four characters before it is formatted. Two tests exist for exactly this, one for orders and one for licences, because the value of the feature is that the model can read those facts. A draft that quotes [[license_key_hcopbefa]] back at a customer is worse than one that says nothing.
The consequence is that this block is the one place where identifying detail is deliberately readable to the model. Two things in it are worth naming. The connected system's own record of the customer's name is a second name channel: the structured masking works from the help desk's contact field, and where the store spells the name differently, the store's spelling is what reaches the model. And the block carries their purchase totals, how many orders they have placed and what they have spent, which are not identifiers but are a picture of the customer.
The block is assembled per draft and is not stored: it is read fresh each time, and what persists is the draft itself, under Section 6's schedule.
Full retention schedule
| Data | Kept for |
|---|---|
| Generated reports | Your plan's report retention: 90 days (Trial), 90 days (Assist), 365 days (Pro), unlimited (Scale) |
| Draft replies, triage results, alerts | 180 days |
| Simulation results | 180 days |
| Search-query logs | 90 days |
| Indexed knowledge base | For the life of the workspace. Removed by erasing a customer, or by deleting the workspace |
| A record that Overdesk sent a reply: the conversation and message it went to, and a fingerprint of the reply, never its text | As long as the workspace exists. It lets Overdesk recognise a reply it sent after the draft is deleted at 180 days. Removed by erasing a customer, or by deleting the workspace |
| Billing and usage records | As long as tax and accounting law requires |
Session cookies
Session cookies are HttpOnly, Secure, SameSite=Lax, and expire after 24 hours with no sliding renewal.
Workspace-scoping test gate
Every query against workspace data is scoped to a single workspace, and a build-time test gate fails the build if a new query is written without that scope. Three deliberate exceptions are enumerated and reviewed rather than incidental: signing you in before we know who you are, resolving a Stripe customer, and deliberately global jobs such as the retention sweep. None of them can put one workspace's support content in front of another.
Reporting
Found something these notes get wrong, or a gap between them and the product? Email security@overdesk.io. The vulnerability-reporting terms are on the Security page.