PhishTriage — Privacy Policy
Effective date: 2026-10-04 Version: 1.10 Applies to: PhishTriage browser extension (Chrome, Edge, Firefox, Brave, Safari) version 1.1.0 and later, and the default backend services at https://api.phishtriage.com and — only with file protection switched on — https://filescan.phishtriage.com.
This line used to stop at Brave, while the file-protection section below discussed Safari as a platform the feature does not reach. A build we ship was missing from the list of builds this policy covers, so here it is, with the two ways it differs: the Safari build has no file protection and no administrator policy. Safari implements neither the download APIs the first needs nor the managed storage the second is delivered through, so every sentence in this document about file protection, and every sentence about what an administrator can set for a managed fleet, describes the other four builds and not that one.
A second client reaches the same backend, and until version 1.5 no version of this document said so. api.phishtriage.com also answers the PhishTriage message filter on iOS, which sends text messages for a verdict. It is not the browser extension, and nothing in the extension can read your messages on any platform. But it is the same proxy, the same analysis path and the same operator, and an entire content-bearing surface was described nowhere. It is described here now, under Text messages. That section covers the message filter and does not attempt to be a full policy for the iOS app.
TL;DR
- We don't send a page anywhere until you click Analyze. Two things of ours run before that. On the five mail hosts named below, our content script loads on every page you open there and watches the page's structure so it can keep the Analyze button in front of you. And with file protection enabled — by you, or by your administrator's policy on a managed fleet — the extension reads the bytes of files you download, on your device, in order to check them. This bullet used to say "we don't read a page until you click Analyze" and name file protection as the one exception; the mail hosts were the other one.
- When you click Analyze, the page or email content you're looking at is sent to our backend, which passes it to a model for a verdict; we show you the verdict in the sidebar. Which model, and whose hardware it runs on, changed after version 1.4 was written. The backend now asks a model we host ourselves first, and reaches Anthropic's Claude API only when the tiers in front of it do not produce a usable answer. This bullet used to say the backend "forwards it to Anthropic's Claude API for the AI analysis", full stop, which over-states how often your content reaches Anthropic and says nothing about the machine that now sees it first. Anthropic remains a processor, and on a bad day it is still the one answering. You will not find a sentence in this document promising your content stays on our hardware; Where analysis happens sets out the order and the conditions instead, and says why.
- If you also use PhishTriage on iOS, your text messages are a separate surface with separate rules. The browser extension does not read text messages and has no permission that could let it. The iOS message filter sends the sender and body of some messages to the same backend for the same kind of verdict. No version of this document mentioned it before version 1.5; Text messages does.
- Unless you turn it off, an Analyze that comes back phishing or suspicious also sends us evidence: on a web page, a screenshot of that tab and the page's HTML, so the site can be reported for takedown; on Gmail and Outlook Web, a screenshot cropped to the message you analyzed and that message's HTML — never your inbox, and never the message's address in your mailbox. This and the feedback buttons in the next bullet are the two things here that are on before you touch anything. It is shown to you during setup and it lives in the popup under ⚙ Settings as "Keep evidence of phishing". Nothing is sent on a clean verdict. Webmail on any other host counts as a web page, and the screenshot there is of your whole inbox. Up to version 1.8 this bullet said nothing was sent on Gmail or Outlook Web. That is set out in full under Keeping evidence of phishing pages, and it is the paragraph to read if you use Proton, Yahoo, Fastmail, Zoho or your employer's own webmail. This summary had no bullet for this feature at all, which put something that ships switched on in the one part of the document a reader is most likely to skip.
- New in 1.7: pressing "Looks safe to me" or "Looks dangerous" under a verdict sends that answer to us. Up to version 1.6 those two buttons kept your answer on your device and sent nothing. They now send it by default, and an organisation can switch the sending off for its managed fleet by policy; there is no switch for it in the popup. Nothing is sent until you press one. What is sent is which button you pressed, the verdict you were shown, the time and — for a web page only — the page's address. A report you make on mail never carries the message's location. Your corrections to a verdict below says exactly what we keep, for how long, who can see it, and what reaches the intelligence hub, which on the hosted service, as at the effective date at the top of this document, is nothing.
- Also new in 1.7: we run a shared intelligence hub, and it is a third destination for data derived from your scans. It is ours rather than a third party's, and it never receives message content. What it can receive is link URLs with any per-recipient part removed, domains, hosts, attachment digests and a pseudonym that cannot be reversed, each filed under the organisation it came from, with the time, and, where the file scanner is connected, fingerprints of downloaded files its deep scan found to be malware. Every flow to it is off in the shipped configuration, and is off on the hosted service as at the effective date at the top of this document. It is described here before it is switched on, which is the only order worth anything; Shared threat intelligence below is the section, including how long the hub keeps what it is given, when it lets go of the pseudonym that links it to you, what still points to you after that, and what your "Delete my scan history" reaches there.
- If you switch on Background protection, which is domain visit tracking (off by default), the hostname of every page you visit is also sent to the backend for a threat-intel lookup. You can disable this any time — unless your administrator has set it by policy on a managed fleet, in which case you cannot. You can revoke the underlying permission from your browser's extension settings. The popup calls this switch Background protection, and from extension version 1.1.0 so does the welcome page you see at install. Up to version 1.0.0 of the extension that welcome page calls the same switch "threat monitoring", and so did this bullet up to version 1.9.
- The extension does not read your identity from the browser. There is no
identitypermission in its manifest, so it cannot read the account your browser is signed in with, and it never asks you to type an address into it. Until you sign in, your install is identified by a random string that is not derived from anything about you (see Pseudonymous ID). If you do sign in, from the popup's Log in button, the extension is told your account id and your display name, and shows "Signed in as" that name. The sign-in response does not include your email address. This bullet used to open "the extension does not learn who you are", which was a bigger claim than the manifest fact underneath it supports. - But what you scan can still name you. On Gmail and Outlook Web the recipient address of the message you analyze is normally your own, and it is sent along with the sender, the reply-to and the body. On a web page the full URL is sent, and a URL can carry an account name, a search you ran, or a session token. The extension does not go looking for who you are; it sends what is in front of it, and what is in front of it is often enough. This is why the Firefox build declares
personallyIdentifyingInfoas required data collection, and why that declaration does not contradict the bullet above it. - The service is a separate question, and this summary used to answer it wrongly. It said "we don't know your email address, name, IP address, or login info". That was written before the product had accounts, and it is not true of the service today:
- Email address and display name — held only if you sign in to the portal, which the popup's Log in button opens. Sign-in is Google, and we store the email address Google returns, its "verified" flag, your Google Workspace domain if you have one, and your display name. If you never sign in, no identity record is created for you — with one way round it: an organisation owner who invites you types your address into the invitation, so it is stored from that moment whether you accept or not.
- IP address — every request to our backend carries one, as every HTTP request does. Alongside a stored record we keep only a truncated hash of it, but the raw address is written to the server's application log on each triage. Visit pings log the hash only.
- Login info — we never see a password: you authenticate on Google's pages, not ours. We do store session, device and refresh tokens, hashed.
- The backend stores triage requests and visit records for operational purposes (caching, abuse detection, threat intel), a record of each file check if you use file protection, and the corrections you send with the buttons under a verdict. The hosted service deletes scan, visit and file-check records automatically. Each is given its deletion date when it is written: 365 days later by default, or after a period from 7 to 365 days that you or your team's owner chose in the portal. A record keeps the period it was given, so one written before we switched the hosted service to 365 days, on the effective date at the top of this document, keeps the period that applied then — 90 days for a scan or visit record (see Retention). The corrections are deleted after 90 days, whatever period applies to your scans. You can delete your own history from the portal at any time — details below. Up to version 1.9 this bullet said the hosted service deleted these records after 90 days, and it did not mention file-check records, which nothing deleted. Several things sit outside those periods, and each says so in its own section rather than here: the server's application log, which nothing in the application deletes; evidence captures, which run on their own clock of 12 months or 30 days; your device records, which have no expiry at all; your account and identity records, which have no automatic expiry either; and the record we keep each time someone changes a retention period, which has none. Up to version 1.7 this bullet also named records from before our August 2026 migration, kept in a retired datastore with no sweep, and it counted four exceptions without the account and identity records, which version 1.8 added. We deleted that datastore on 2 October 2026. This bullet used to say the 90-day rule covered everything the backend keeps, and the sections below had been contradicting it for two versions when version 1.4 corrected it.
This paragraph used to offer "one number to remember: activeTab as the only required permission". The manifest inside the package a reviewer downloads says otherwise, so here is the real list.
Required at install: activeTab, storage, scripting and, from extension version 1.1.0, alarms, which lets the extension wake itself to ask again about an enrolment the backend has not yet answered (see Pseudonymous ID) — plus host access to five mail hosts (mail.google.com, outlook.office.com, outlook.office365.com, outlook.live.com, outlook.cloud.microsoft), which the extension declares as static content-script matches and which your browser therefore grants when you install it, naming them in the install prompt. The fifth, Outlook's newer web address, is new in extension version 1.1.0, so the update to 1.1.0 asks for it: Chrome and Edge keep the extension switched off until you accept. Up to version 1.8 this paragraph listed four hosts.
Optional, requested only when you switch on the feature that needs them, and revocable from your browser's extension settings — chrome://extensions → PhishTriage → Details → Permissions on Chrome, Edge and Brave, about:addons → PhishTriage → Permissions on Firefox: webNavigation for Background protection, downloads for file protection, and <all_urls> for either — both features ask for it, and turning on just one of them is enough to be prompted for access to every site. With neither switched on, no host access beyond the five mail hosts above is ever requested. Up to version 1.9 this paragraph called Background protection "threat monitoring".
Who we are
PhishTriage is operated by the PhishTriage team. For privacy questions, data-deletion requests, or anything else covered by this document, write to privacy@phishtriage.com.
The data controller (for purposes of GDPR / UK GDPR) is the PhishTriage team. If your organization has deployed the extension with a self-hosted proxy (see "Self-hosting" below), the data controller is your organization — not us — and this document only describes the client-side behavior; ask your IT or DPO for their corresponding policy.
What the extension collects, and when
When you click "Analyze" (on-demand triage)
Pressing the Analyze button in the popup, in the sidebar, or in the toolbar of Gmail or Outlook Web is what causes the page or message in front of you to be read and sent for a verdict. When you press it, the extension reads the tab — through the activeTab permission on an ordinary page, and through the content script already loaded on the five mail hosts, which needs no activeTab — and sends a request to the configured proxy (default api.phishtriage.com) containing:
- On Gmail and Outlook Web (the five mail hosts named under Keeping evidence of phishing pages): the subject, sender address and display name, recipient address, reply-to address (on Gmail; Outlook Web does not show one), date, the text body, up to ten embedded URLs, and the filenames of attachments. Attachment contents are never read. From extension version 1.1.0 the text body is the message's own text and then, after a line reading "--- quoted text ---", its quoted and forwarded parts and, on Outlook Web, its signature. If the whole is longer than 10,000 characters, the quoted part is cut first, though at least 2,000 characters of it are kept, and the message's own text is shortened where that is what it takes. Up to version 1.0.0 of the extension the body is the message's own text alone: its quoted and forwarded parts, and on Outlook Web its signature, are left out. Up to version 1.9 this bullet named Gmail alone and said the body was sent "with quoted prior thread stripped".
- On any other webpage: the page title, the full URL of that page — including path and query string — the hostname, up to 10,000 characters of text content — not only the text you can see. Scripts, styles and PhishTriage's own injected panels are stripped, but text the page has hidden with
display:none,visibility:hiddenor thehiddenattribute IS included, because a page that hides text from you and shows it to a machine is doing something worth knowing about. This paragraph said "visible text" until 2026-08-24 and that was not accurate: the extractor reads a detached copy of the page, and a detached copy is never rendered, so there is no rendering for "visible" to be judged against. Also sent: up to fifteenhttp(s)addresses from the page. Up to version 1.0.0 of the extension they are the first fifteen links in it. From extension version 1.1.0 up to five of them are where the page's forms send what is typed into them (a form's own address, or a button's), each cut to its origin and path, with no query string and at most 96 characters, the forms that ask for a password first; the rest are the page's links. Text and links inside frames embedded in that page are included, where the browser allows the page to read them at all — a frame served from another site stays unreadable to us exactly as it is to the page itself, and we send only a count of how many such frames were large enough to be holding what you were looking at, plus a single true/false saying the page had a frame while carrying no title and almost no text of its own — the shape of a wrapper that exists only to hold something else. Neither carries any content. This matters because a page can put everything you see one level down: a wrapper whose whole body is a single frame used to reach us as an empty document, and a phishing site that reads as blank is the one case where reading nothing is the worst possible outcome. If a page embeds something of yours from the same origin — a preview, a webmail composer, a document viewer — its text is part of what is sent, and where the page itself has no title, that frame's title is sent as the title. A document viewer's frame title is often a filename, so that is worth stating rather than leaving inside "the page title". The URL is sent because it is one of the strongest phishing signals there is; note that if the page you analyze has a session token or similar in its query string, that string is part of what gets sent. The same goes for a search: analyze a results page and the query you typed travels inside the URL. We do not extract either, and neither is treated as anything but part of the address — but both are sent, and a URL is not a neutral thing to hand over. This applies only to the single page you pressed Analyze on. Other pages may still be observed or reported by the mail content script, Background protection, or file protection as described in their sections below, and managed policy can force the latter two features on.
The payload also carries your pseudonymous ID (see below) and, if you've registered or linked an account, your extension ID. The proxy passes this content to a model and returns a structured verdict (risk score, IOCs, recommended actions). Which model, on whose hardware, is set out under Where analysis happens.
This paragraph used to read "the proxy forwards a sanitized version of this content to Anthropic's Claude API for analysis", and both halves need correcting. The first half is the routing: Anthropic is no longer the first thing asked and is often not asked at all. The second half is the word sanitized, which was carrying an implication it cannot support. It reads as though we strip something out for your sake. What it actually names is a pass that removes prompt-injection markers — [INST], <|…|>, Claude turn delimiters, XML-ish <system> tags, and lines that begin like an instruction ("IGNORE ALL PREVIOUS…") — before a model reads the content, plus the length caps stated above. It exists to stop a hostile email from giving orders to the analyst. It removes those patterns, and where a line begins like one it takes the rest of that line with it. Three smaller changes are made as well. In an email body, the long runs of invisible characters that newsletters put after their preview line, so that the inbox preview does not show the body, are collapsed; nobody reading the email sees them. In the fields shown on a single line, such as the sender, the subject, the links and the attachment names, a line break becomes a space. Those two are in what the model reads and in the request we store. The third is made only in the copy the model reads, not in what we store: where the message contains either heading we put above our own findings for the model, "Facts about the message" and "Precomputed technical signals", that copy marks it "(quoted)", so the message's text cannot pass for ours. Nothing is removed for being about you: no address, no URL and no name is stripped on privacy grounds. What reaches the model is what is listed above, minus injection markers and that padding, with those line breaks as spaces and those headings marked.
If you do not press Analyze, no page text is read or sent for an Analyze verdict. Background protection, and Send full URLs under Advanced, may still send the current hostname or URL for a threat verdict, and file protection may process downloads, as described below.
Analyze is not the only thing that sends, and this section used to say it was. Besides the visit checks just mentioned, three things do, and each has its own section below. With file protection on, every file you download is read on your device and a fingerprint of it — with its name, its size and the reasons behind our verdict — is sent, and with "Deep scan every file" on the file itself is sent; none of that waits for a press. From extension version 1.1.0 a file that a page builds inside the browser is the exception: for it, the fingerprint and the file are sent only when a person started that download (see the file-protection section). Evidence capture, which is on unless you switch it off, sends a screenshot and HTML when an Analyze comes back phishing or suspicious — of the page, on a web page, and of the analyzed message alone on the mail hosts. And since version 1.7 the two buttons under a verdict, Looks safe to me and Looks dangerous, send your answer when you press one; see Your corrections to a verdict. Until version 1.7 this paragraph named two things, because those buttons sent nothing. The sentence above used to read "nothing on the page leaves your device", which was written before file protection shipped and stopped being true when it did — a blob: file a page builds is something on the page by any reading.
That sentence used to be followed by another, "the extension does not run on pages you haven't asked it to", which was never true of the mail hosts and is not true of file protection either. Code that runs is not the same as data that leaves, and the honest version of the running half is:
- On
mail.google.comand the four Outlook hosts, our content script is declared in the manifest and loads on every page you open there, atdocument_idle, and injects the sidebar. It is what puts the Analyze button in front of you. It reads the message and sends it only when you press that button. The rest of the time it is not idle, and this document used to say it was: it watches the page for changes so it can put the button back when the mail app rebuilds its toolbar, which means it reads the page's structure continuously from the moment it loads. It reads no message content, and sends nothing at all, until you press Analyze. - With file protection switched on, two further script registrations — four files between them — are attached to
<all_urls>atdocument_startin every frame, so that a file a page manufactures can be examined before it reaches you. This is the feature's whole mechanism; it cannot work on pages nominated in advance. Those scripts, and the download watcher behind them, are also the thing here that sends without a press — see the file-protection section for exactly what. With file protection off, which is how it ships, none of it is registered at all. - On any other page, with file protection off, nothing of ours is loaded until you press Analyze. The injection rides the
activeTabgrant, which the browser ends when you navigate away. From extension version 1.1.0 there is no exception to that: when Background protection finds a page you have just navigated to dangerous, the extension sends the tab to its own warning page and injects nothing into the flagged page. Up to version 1.0.0 of the extension there is one exception, the interstitial: if you turned Background protection on and the backend calls a page you have just navigated to dangerous, the extension injects the content script there unasked, in order to put the warning in front of you.
When you opt into URL or Domain Visit Tracking
These are off by default for you. In the popup they are the Background protection switch (domain tracking) and, under Advanced, Send full URLs (URL tracking). If you turn either on there or from the welcome page on first install, Chrome (or your browser) prompts you for permission to "read your browsing history" (webNavigation) and to "read and change all your data on websites you visit" (<all_urls>). You can decline the prompt, in which case the toggle reverts to off.
On a managed fleet these two are not yours to decide, and this section used to be silent about that. An administrator can force either one on — or off — with the trackVisits and trackDomains policy keys, and a forced value beats the popup: the setting you save is read back with the policy value on top of it, so turning it off changes nothing about what is sent. Forced URL tracking means the full URL of every main-frame navigation, query string included, goes to the proxy on every page you load — and, from extension version 1.1.0, every address a page moves to without loading a new document, as described below. The popup does show you when this is the case — a control an administrator has set is drawn with a Managed badge and cannot be switched, and a notice at the top of ⚙ Settings says so. From extension version 1.1.0, forcing either one on does not start it by itself: nothing is checked or sent until you allow the browser's webNavigation permission, which the popup, and the welcome page at install, ask you to do. The document disclosed this override for file protection, for deep scan and for evidence capture, and before version 1.4 named it nowhere for visit tracking, which is the one place a person is most likely to assume the switch is theirs.
With Domain Visit Tracking enabled, every time you navigate to a new page, the hostname (e.g. example.com) is sent to the proxy's /visit endpoint with type domain. The full URL is not sent. From extension version 1.1.0 a page is checked as soon as the browser starts to show it, rather than once it has finished loading, so a page that never finishes loading is checked too.
By default the hostname is sent on each navigation. An optional local cache — off by default, switched on under Advanced in the popup — remembers the answers. From extension version 1.1.0 a site found safe is not asked about again for an hour. One flagged as dangerous is asked about again after 15 minutes, and a return visit before then shows the warning again without asking. A check that got no answer is not cached, so the next visit asks again. Up to version 1.0.0 of the extension the cache limits this to at most once per hour per domain, however many pages you load on it, whatever the answer was.
With URL Visit Tracking enabled, the full URL (including path and query string, and anything after a #, as your browser reports it) is sent on each main-frame navigation, with type url. From extension version 1.1.0 it is also sent when a page changes its address without loading a new document (history.pushState, history.replaceState, or a change after the #), one request at a time for each page, for the latest address it has moved to. Up to version 1.9 this paragraph named neither the part after the # nor those changes. This is the more invasive option; we ship it off by default for a reason. Enable it only if you want full-URL-level threat intelligence.
Both endpoints respond with a threat verdict. From extension version 1.1.0, if the proxy flags a URL or domain as dangerous, the extension replaces the page with its own warning page, in the same tab. Go back to safety goes back through the tab's history when that is known to lead past the flagged page, and otherwise opens your browser's new tab page. Choosing to continue to the site loads the flagged page once, in that tab only: the extension remembers that one address for that one tab, in the browser's session memory and never on disk, until the browser next starts to show a page in that tab, the tab closes or two minutes pass, whichever comes first. The warning page's own address carries the flagged address, so the flagged address is in your browser's history, as the flagged page itself already was. Up to version 1.0.0 of the extension a full-page interstitial warning is drawn over the flagged page itself; you can choose to go back, or to proceed anyway.
When you opt into File protection (early access)
File protection is off by default. It is available on Chrome, Edge, Brave (which runs the Chrome build) and Firefox, and not on Safari, which does not implement the download APIs it needs. Turning it on asks for two permissions together: downloads, and access to all sites. Both are needed — the part that catches a file a page builds inside the browser reads page content, so granting only downloads would cover less than this section describes. Where an administrator has forced file protection on, from extension version 1.1.0 it does not run until you have allowed both, and the popup, and the welcome page at install, ask you to.
By default the file itself stays on your device. This line used to read "no file and no part of any file leaves your device", and the second half of that was not true: one of the short reasons we send with the fingerprint names a file found inside an archive you downloaded, so a little of what is in the file can travel with it. What is sent is set out two paragraphs down. When you download a file the page created (a blob: or data: download — the shape used to smuggle files past email filters), the extension reads its bytes in your browser's memory, checks them there (is it an executable dressed as a document? an Office file carrying a macro? an executable inside an archive?), and warns you — or, in block mode, holds the download and asks first. Those bytes are discarded straight after the check and are never written anywhere by the extension.
What is sent by default: a SHA-256 fingerprint of the file (a 64-character hash), its filename and size, the extension's own verdict, and the short reasons behind that verdict. The fingerprint is checked against known-malware fingerprints. A hash is 32 bytes and cannot be turned back into your file — it lets us recognise a file we already know is malicious without ever seeing yours. The reasons are mostly fixed phrases the extension wrote ("runnable script (.js)", "Office document with an embedded macro"), and some quote the file's own name or extension, which are sent anyway. One of them quotes something that is not: the name of an executable found inside an archive you downloaded. Download invoice.zip with payload-for-acme.exe inside it and that inner name is sent with the fingerprint, without the file and without a second switch. The reasons were missing from this list until version 1.4, which is what made the "no part of any file" sentence above look true.
From extension version 1.1.0, a file a page builds is sent about only when a person started the download. That means your own click on the link, or a click the page makes while your click is still live; a browser keeps a click live for about five seconds, so a page whose export takes longer than that to prepare counts as having started the download itself. A file a page saves by itself is still checked on your device, held in block mode and warned about, but neither its fingerprint nor the file is sent. Ordinary downloads are not affected. Up to version 1.0.0 of the extension this distinction is not made.
Every check that reaches the file scanner leaves a record with us, including the default one that sends no file: the fingerprint, the file's name (up to 300 characters) and size, the verdict, which check gave it, the deep scan's engine or the name of the malware signature it matched when there was one, your pseudoId and the device's extensionId, when it was made, and the date it will be deleted. It is given its deletion date when it is written, the same way as your scan and visit records; the one exception is file checks made before we began giving every record a date, which were given 365 days (see Retention). "Delete my scan history" removes it. A file a page saves by itself, which is checked only on your device, leaves no record here. Your own dashboard in the portal does not show it. In a team, the owner sees it in the Scans list on the portal's Team page, except in an organisation that reports in aggregate, where that list is withheld. Up to version 1.9 this document did not describe this record, and said we kept "only the fingerprint and the verdict".
Sending an actual file is a separate, per-file choice. When a check is inconclusive, the warning may offer "Check it properly". A file is uploaded only if you click that button, for that one file. It is scanned in memory by our own scanner on our own hardware, the verdict is returned, and the file is not stored — we keep only the record of the check described above.
Unless you turn on "Deep scan every file". That setting is off by default, and it changes the rule above: with it on, every file you download is sent to us for a full malware scan as it arrives, rather than only the ones you ask about. Each file is still scanned in memory and still not stored — we keep the same record of the check, as before — but the file itself leaves your device every time, except, from extension version 1.1.0, a file a page built and saved by itself, as described above. It exists for people and organisations who want everything checked; if that is not you, leave it off and nothing is uploaded unless you ask. An administrator can require it for a managed fleet via policy.
Ordinary downloads (a normal link, a file a server sends) are covered too, but differently: the browser does not hand an extension the bytes of such a download, so the extension fetches the file's address a second time to read it, checks it in memory the same way, and discards it. Practical consequences, stated plainly: the file is transferred twice, and a one-time or single-use download link may fail that second fetch — in which case the check falls back to the file's name and type. The URL is checked for reputation on your device, against a threat list the extension downloads; the address of what you download is not sent anywhere to be looked up.
What this feature does not do: it does not read files already on your disk, does not watch your Downloads folder, and cannot stop an ordinary download before it saves — no browser extension can. For those it warns after the file lands and offers to delete it.
What the extension keeps on your own device
This subsection was new in version 1.4. The document described what we receive and never described what stays with you, and named exactly one storage key — pseudoId — in a way that read as the whole list. Keeping these copies locally does not by itself trigger a transmission; where a listed value is also sent, the relevant section below says so. This list is set out because it is data about you that exists because you installed us, and because the deletion routes further down act on our servers and cannot reach your browser profile.
- Your identity —
pseudoId,extensionId, the organisation and the device tokens described under Pseudonymous ID. From extension version 1.1.0, on a device an administrator has given an enrolment token by policy, also a SHA-256 of that token, the device ID it was sent for, and what the backend answered and when, or, while it has not answered, how many tries have failed and the earliest time for the next; never the token itself. Sign out and Leave team clear these, and the enrolment record also goes when the device registers afresh because the backend no longer knew it. - The ✓ / ⚠ you press on a verdict ("Looks safe to me" and "Looks dangerous"). Up to a hundred of them, each holding the verdict, which button you pressed, the time, whether it was sent, and the full address of the page or mail message the verdict was about — on Gmail, that address identifies the message. From extension version 1.1.0 that is the address the page had when it was read for the verdict, so a page that changes its address before you press does not change it; up to version 1.0.0 of the extension it is the address the page had when you pressed. That address stays in this copy on your device. The report sent to us is a smaller thing, and on mail it leaves the address out; Your corrections to a verdict says what it holds. Until version 1.7 nothing transmitted these at all. Nothing deletes this copy either: not Sign out, not Leave team, and not "Delete my scan history", which is a server-side action. Removing the extension removes it, and so does clearing the extension's storage from your browser's extension settings.
- The URL blocklist we sent you, and when — only once file protection is on. From extension version 1.1.0, where an administrator's policy names an intelligence hub and the keys that sign its lists, also the hub's signed list, as well as or instead of ours (see Shared threat intelligence).
- The names of risky downloads we could not put a warning in front of at the time, up to ten, each with when. Opening the popup clears the toolbar badge, not the list: the list stays until you press Got it in the popup, or until every waiting warning (next item) has been shown to you on a page. Up to version 1.9 this item said opening the popup cleared them.
- The warnings themselves, while they wait to be shown — up to five, each with the download's name, its number in your browser's download list, the reasons behind the warning and when it was queued; from extension version 1.1.0, also whether "Deep scan every file" already sent that file, so the warning can say so. One is shown, and removed, on the next ordinary web page you open, or dropped there instead if it has waited more than an hour. Up to version 1.9 this list did not name them.
- The hostnames you visited recently, each marked safe or flagged — only if you turned on Background protection and its Advanced cache option, because that cache is what the option is. From extension version 1.1.0 each hostname is kept with whether it was found safe or flagged as dangerous, for as long as the cache trusts that answer (an hour for safe, 15 minutes for flagged). Up to version 1.0.0 of the extension it is the hostnames you visited in the last hour, each with the time.
- Whether site checks are failing — from extension version 1.1.0, while Background protection's checks of the sites you visit are getting no answer from our backend: since when, a reason code, and any pause the backend asked for. It names no site, and it is removed when a check is answered again, or when Background protection and Send full URLs are both off. Beside it, the time the extension last tried to register afresh because our backend no longer knew the device.
From extension version 1.1.0 the extension also keeps three things in the browser's session memory, which is never written to disk and is gone when the browser closes:
- A "continue to the site" choice you made on the warning page: that one address, for that one tab, until the browser next starts to show a page in that tab, the tab closes or two minutes pass (see When you opt into URL or Domain Visit Tracking).
- For each verdict shown to you, what a press of ✓ or ⚠ under it may report: the verdict, the address the page had when it was read, whether it was a web page or mail, and which tab, frame and document it was shown in, until you press one of the buttons or a day passes.
- For each download warning shown on a page, the download's number in your browser's download list and the tab it was shown in, so that its Delete it button can delete that file and no other, until the button is used or five minutes pass.
Your settings themselves live in chrome.storage.sync, not local, which means your browser copies them to your Google or Mozilla account along with the rest of your profile if you have browser sync switched on. They are switch positions, not content.
Keeping evidence of phishing pages
This setting is on unless you turn it off. It is shown to you during setup, and it lives in the popup under ⚙ Settings, where you can switch it off at any time. With it off, nothing described in this section ever happens. An administrator can set it either way for a managed fleet with the evidenceCapture policy key.
It is one of two things in PhishTriage that are on before you touch anything, which is why it is described here in full rather than summarised. The other, since version 1.7, is that your answer to a verdict is sent when you press one of the two buttons under it; that sends nothing until you press, and has its own section below. Until version 1.7 this paragraph called evidence capture the only one, which was true while those buttons sent nothing. It used to be described as the only thing that sends without being switched on first, and that is a different and untrue claim: an administrator's policy can switch Background protection, file protection and "Deep scan every file" on for a managed fleet whatever your switches say (from extension version 1.1.0 the first two then wait until you allow the browser permission each needs), and registration — described under Pseudonymous ID — runs by itself at install, before there is a setting to have an opinion about.
A verdict says a page or a message was phishing. It does not prove it to the registrar, host or mail provider who can actually act on it. This setting exists to produce something they can act on.
What is captured on a web page. Two things, and only these two: a screenshot of the visible part of the tab (a JPEG — what you saw, and what a victim would have seen), and the page's HTML as it stood in your browser (what the page actually is: the form that collects the password, the address it posts to, the code it loads). What is captured on the mail hosts is narrower, and is set out below.
When. At the moment you click Analyze — before the verdict exists — because a screenshot taken any later would be of whatever the tab had moved on to, and attaching the wrong page to a takedown request is worse than attaching nothing. Both are then held in the extension's memory only, for the few seconds the scan takes. Neither is written to disk, to browser storage, or anywhere else on your machine.
Whether it is sent. Only if that scan comes back phishing or suspicious. On any other verdict both are discarded and neither leaves your device. Nothing is captured on chrome:// pages, the Chrome Web Store, or other extensions' pages, because the browser refuses.
On the mail hosts, the message you analyzed and nothing around it. The mail path is five hostnames, not a rule about email; that was the most important correction in version 1.4, so it is stated at length. The mail path is exactly these five hosts:
mail.google.comoutlook.office.comoutlook.office365.comoutlook.live.comoutlook.cloud.microsoft
On those five, a picture of the whole tab is never taken. It would be of your inbox — the message list, other people's mail, your folders and your account — which is the most sensitive thing this feature could take and no help in reporting phishing. If the verdict comes back phishing or suspicious, what we receive instead is the message you analyzed, alone:
- a screenshot cut down to that message as it was on screen — on Gmail the open message with its sender line, on Outlook the message's body; anything around it, and any part of it you had scrolled out of view, is cut away before anything is sent; and
- that message's HTML, not the page's. On a phishing message that is what matters: the links, where they really point, and the text that asks for your password.
The address of the message in your mailbox is not sent with it. If the extension cannot find the message on the page, or cannot cut the picture down to it, it sends nothing rather than a wider picture. Your browser lets the extension take the picture only when you opened PhishTriage from the browser's toolbar for that tab, or when you have given it access to all sites, which switching on Background protection or file protection asks for; a press of the PhishTriage button inside Gmail or Outlook on its own captures nothing.
Up to version 1.8 this section said nothing at all was captured on the mail path. Extension version 1.1.0 is the first to capture the message, and the first to check the address of the tab against the list before it takes any picture, so a page on one of these five hosts is never photographed whole even if the extension has not recognised it as mail. Up to version 1.8 the list also had four hosts and left out outlook.cloud.microsoft, Outlook's newer web address, so Outlook read there fell under the next paragraph as an ordinary web page.
Webmail anywhere else is treated as an ordinary web page. The check is a hostname comparison, not a judgement about whether you are reading mail. So if you use Yahoo Mail, Proton Mail, Fastmail, Zoho Mail, Roundcube, or your employer's own Outlook Web Access on a company domain — anything that is not one of the five hosts above — then pressing Analyze on a message there is pressing Analyze on a web page. If the verdict comes back phishing or suspicious, we receive:
- a screenshot of the visible part of that tab — your inbox as it looked at that moment, including the message list, the sender names and subject lines on screen, and any part of the open message you could see; and
- the full HTML of the page, unfiltered. Not the 10,000-character text extract used for the verdict — the whole document as it stood in your browser, which on a webmail client includes the content of the message you had open and whatever else the page had rendered around it. There is one limit: a capture is abandoned if it exceeds 1 MiB compressed. It is dropped whole, not trimmed — so a very heavy page produces no evidence rather than partial evidence.
This is the shipping configuration. Evidence capture is on unless you turn it off, and "suspicious" — the verdict class this document elsewhere calls the home of false positives — is enough to trigger it. Nothing about this requires you to have opted in.
To turn it off: open the PhishTriage popup, click ⚙ Settings, and switch off Keep evidence of phishing. With it off, nothing in this section happens anywhere, on any host. If you read mail on a provider that is not one of the five above, that switch is the one to look at.
This paragraph used to read "nothing is ever captured on the email path", full stop, and the sentence was written when Gmail and Outlook were the only mail the product had ever been pointed at. It read as a promise about email. It was only ever a promise about a list of hostnames, and the gap between those two things is somebody's inbox. The extension's own enterprise-policy schema has described the behaviour correctly — "webmail on any other host counts as a web page" — while this document denied it; we are fixing the document, not the schema.
Two honest caveats. Suspicious is where false positives live, so with this on you may upload a page that turns out to be perfectly legitimate — that is why suspicious pages are kept for a much shorter time, and why a reviewer who says it was not phishing deletes it on the spot — though inside an organisation that review is refused while the activity window is withholding, which is its default state; see the retention section. And the HTML usually does not contain what you typed, because typed values live in a property rather than in the page's markup, but a page can write them back into the markup, so usually is the honest word.
How long it is kept. Not by the scan-history period below, which is about your own scan history. Evidence is about a third party and is kept on its own clock, whatever period applies to your scans:
- Phishing — 12 months. A takedown, a re-listing and a dispute can each outlast 90 days, and this is the artifact that ends them.
- Suspicious — 30 days. Short enough that a screenshot nobody has looked at does not sit for a year.
- Rejected by a reviewer — deleted immediately. If a person reviews it and says it was not phishing, keeping it would be storing a mistake. A suspicious page a reviewer confirms moves onto the 12-month clock, because what changed is the classification, not the thing.
- Deleted with your account, along with everything else.
Who can look at it, and how. If you scan on your own, only you. If your account belongs to a team, then everyone on that team — the same people who can already see that the scan happened.
It appears in the portal in its own Phishing Evidence list on the dashboard, alongside the scan it came from while that scan still exists. The list is the part that matters, because evidence can outlive the scan it came from: once the sweep removes the scan record, the capture is still there, and the list is how you reach it to look at it, download it, or delete it. This paragraph used to say evidence was visible "on the scan it belongs to", and for the longer half of a 12-month capture's life that was not true of anything you could click.
Except in an organisation that reports in aggregate, where the list is withheld from everyone. Aggregate is the mode every organisation starts in. In it, a list of captures is per-person activity like any other, so the portal refuses the whole surface — not just the seeing. Nobody, the owner included and the person whose own device made the capture included, can open the Phishing Evidence list, download a screenshot or its page source, confirm a capture, reject one, or delete a single one. This document conceded only the seeing half and left "look at it, download it, or delete it" standing next to it.
Who can delete it. Inside a team, confirming or rejecting a capture is an owner's action — and only where the organisation reports attributed; in aggregate mode, per the paragraph above, nobody can. What works in every mode is deleting your own: the "Delete my scan history" button described under Retention removes every capture stored under your account, including ones whose scan record has already expired, and no organisation role can do that to you or undo it.
That button is scoped to your account, not to your hardware. This document used to say it removed "every capture made on your own devices", which is a different set: a capture your device made before you linked it to your account was written under that device's own prior identity, and the button does not reach it. That is the same edge the Retention section already states for scan and visit records, and it applies to captures too.
The page's own code is never run. Stored HTML from a hostile page is treated as hostile: it is never rendered in our portal, only downloaded as an inert file, and it is served as plain text so that opening the link cannot execute it. The screenshot is a picture and is checked to be one before it is stored — a file that is not really an image is refused.
Your corrections to a verdict
This section was added in version 1.7. Under every verdict the sidebar shows two buttons, ✓ Looks safe to me and ⚠ Looks dangerous. Up to version 1.6 they kept your answer on your device and sent nothing. They now send your answer to us as well, and that is the default. Nothing is sent until you press one of them: the press is the whole of the trigger, and a verdict you do not answer produces no report.
There is no switch for this in the popup. An organisation can turn the sending off for its managed fleet with the feedbackUpload policy key set to false, and the answer is then kept on the device only, as it was before. The Safari build has no administrator policy (see the top of this document), so there it cannot be switched off that way. If you do not want a correction sent, do not press the buttons.
What the extension sends. One small request to api.phishtriage.com per press, holding:
- which button you pressed: safe or dangerous;
- the verdict you were shown: phishing, suspicious or benign;
- the time you pressed it, by your device's clock;
- that the report came from the extension;
- on a web page only, that page's full address, query string included; from extension version 1.1.0, the address the page had when it was read for the verdict, not wherever it has moved since.
Nothing from the page or the message itself goes with it: no text, no title, no sender, no recipient, no subject. Like every other request from the extension, it also carries the device credential the extension registered with, which is how we know which device and which account a report came from. A device that has not registered cannot send one; we refuse it rather than keep a report nobody can be attributed to.
A report made on mail never carries the message's location. On Gmail and the four Outlook hosts named under Keeping evidence of phishing pages, the address in your browser identifies your mailbox and the message you have open, so the extension leaves it out of the report. Up to version 1.8 this paragraph said the extension read outlook.cloud.microsoft as an ordinary web page and left the address out there all the same; it is now one of the mail hosts. If a report arrived with an address on one of those five hosts anyway, or with any address at all from the Google Workspace add-on, which sends us none, we would discard the address before storing or forwarding anything. Webmail on any other host is a web page to the extension, exactly as it is for evidence capture, so a report you make there carries that page's address. The copy kept on your device is not the report: it holds the full address wherever you pressed the button (see What the extension keeps on your own device).
What we keep. One row per report, in the database that holds your scan history:
- your pseudoId (the identifier described under Pseudonymous ID, which is your account's id once you sign in) and your device's extensionId;
- which button you pressed, and the verdict you were shown;
- the time your device gave, recorded as a claim, and the time we received the report;
- which client sent it;
- whether it was forwarded to the intelligence hub;
- a keyed hash of the page's address, only where the key for it is configured. That key is part of the intelligence hub's configuration, which is not set on the hosted service as at the effective date at the top of this document, so there this field is empty and nothing derived from the address is kept at all. Where the key is set, the hash is taken of the address cut down to its scheme, host and path (no query string, nothing after a
#), and is left empty where the path looks like it carries a per-recipient token. It lets us see that several people reported the same page without keeping a list of pages, and it cannot be turned back into the address by anyone who holds the rows but not the key.
The address itself is never stored. Neither is your IP address: the request's address is used in memory only, to limit how many reports can arrive from one address in an hour, and the device has a limit of its own. The application log does not record reports. It gets a line only when something goes wrong with one — an address discarded, a row that could not be written — and, the first time after a restart that a report carries an address, a note that no key for the hash is configured. None of those lines carries the address, your pseudoId or your IP address (see The application log). A report is not sent to any model and not to any third party.
How long. On a 90-day clock of its own, whatever period applies to your scan history: the retention sweep deletes reports older than that, several times a day, and "Delete my scan history" in the portal deletes every report stored under your account at once. The edge described under Retention, for records a device made before you linked it to your account, applies to reports too. Evidence's longer clocks do not apply to them. Up to version 1.9 this paragraph put reports on the same 90-day clock as your scan history, which was one clock while scan history had only one period.
Who can see it. No page of the portal shows a report — not to you, not to your organisation's owners, not to anyone. Reports are read only by us, from the database. An access request (see Your rights) returns yours with the rest of your records.
What reaches the intelligence hub. By default, nothing. Forwarding reports to the hub is a separate switch on our side, off in the shipped configuration and, as at the effective date at the top of this document, off on the hosted service. Shared threat intelligence describes the hub and exactly what a forwarded report carries, and each row records whether its report was forwarded.
What the extension never collects
Two of these bullets carry qualifications now, and the heading should be read with them rather than over them. Each names a specific thing the extension has no mechanism to obtain; none of them is a promise that nothing of the kind ever reaches us by another route, and where a route exists the bullet says so.
- The email address on your browser account — there is no
identitypermission in the manifest, so the extension cannot read the address your browser is signed in with, and it never asks you to type one in. Older versions hashed an email address to derive an identifier; that was removed in May 2026. This is not a claim that no email address is ever sent. This bullet used to be headed simply "Email address", and everything beneath it was about theidentitypermission — each sentence true on its own, and the heading read by everyone as the broader promise. On the mail path the message's sender, recipient and reply-to addresses are all part of the payload described above, and on a message in your own mailbox the recipient is normally you. - Passwords or authentication tokens — the Analyze flow reads rendered text content, not input fields, so what you type into a form is not in the payload we send for a verdict. Two qualifications, both about evidence capture and both only when it is on: the screenshot is a picture of the visible tab, or on the mail hosts of the analyzed message, so anything you have typed and can still see in it is in it, and a page can write a typed value back into its own markup, where the captured HTML would carry it. Either is sent only when your own Analyze comes back phishing or suspicious. The markup half of this was already stated in the evidence section; the screenshot half was stated nowhere before version 1.4.
- Browser history — the extension does not enumerate or read your history.
webNavigation(if you switch on Background protection or Send full URLs) fires on future navigations only. - Full page HTML, unless evidence capture is on — content extraction for a verdict is limited to text, link
hrefs and, from extension version 1.1.0, the origin and path of the addresses the page's forms send to, and capped at 10,000 characters for web pages, counted across the page and any frames in it that were readable. Text is not filtered by whether it was on screen; see the webpage bullet above for what that does and does not mean. The single exception is the setting described just above, which is on unless you turn it off, and which sends the page's HTML and a screenshot only when your own scan of a web page comes back phishing or suspicious. On the mail hosts it sends the analyzed message's HTML and a picture of that message, never the page's. - Anything from sites you did not actively analyze — with Background protection, Send full URLs and file protection all off, which is how the extension ships, the extension has no footprint on general browsing beyond the five mail hosts named above, where its content script loads on every page you open and watches the page structure whether you scan or not. This bullet has been corrected twice. It first claimed no passive footprint at all and set that condition on monitoring alone; the second pass added file protection and called it "the other half", which asserted there were two halves when there are three. The mail hosts are the third, and they are not conditional on any setting. With file protection on, scripts run on every page (see the Analyze section), every download is re-read and fingerprinted, and a download makes the device fetch its URL blocklist afresh whenever the list it holds is more than an hour old or it holds none — from our proxy and, from extension version 1.1.0, first from an intelligence hub where an administrator's policy names one. All of that is disclosed in the file-protection section; none of it is "no footprint".
Text messages (PhishTriage for iOS)
This section was new in version 1.5. It covers a surface no earlier version mentioned at all — searching version 1.4 for "SMS" or "text message" returns nothing, while the backend had an endpoint for them the whole time. Nothing in it was a change in behaviour. It closed a gap in the document, and the largest one that version closed.
This is not the browser extension. What follows happens only if you installed the PhishTriage app on iOS and switched its message filter on. If you have not, none of this applies to you. The extension cannot read messages on any platform.
iOS decides which messages the filter is even offered. A message-filter extension does not get your message history and cannot go looking; the system hands it individual messages from senders it does not already trust, and that rule is the operating system's, not ours. We cannot widen it.
Only a message with a link in it is sent to us at all. Before any network request, the app checks the message for a link. A message with no link — and a message with no body — is allowed on the device, and nothing about it is sent. Link detection is the system's and errs toward finding one.
What is sent, when a message is sent:
- the sender as iOS reports it — a phone number or short code;
- the message body, in full;
- the app's version string.
All three arrive complete and are cut down on our side, on receipt — the sender to 100 characters, the body to 2,000, the version string to 40 — before anything else touches them. We state it that way round deliberately: the phone sends the whole message, so those limits bound what we keep and analyse, not what leaves your device. One of the two request shapes our own app can send also carries a small integer version number for the format itself. It describes the message, not you.
That is the entire request body. It carries none of the identifiers used everywhere else in this document: no pseudoId, no extension ID, no device id, no account. The message filter cannot attach one — Apple's deferral does not carry it — and we did not invent a substitute. The endpoint is unauthenticated for that reason, and rate-limited by IP address instead. Which is the honest qualification: your IP address is not in the body, but it arrives with the request as it does with every HTTP request, it is what the rate limit counts, and it is written to the log described below.
But the message can still name you. This is the same caveat the Analyze path carries, and it is just as true here. A text message often opens with your name; a delivery notice or a bank alert can carry an account reference, a booking number or an address. We do not go looking for any of it and none of it is stored — but it is in the body, and the body is sent.
Not every message we receive reaches a model. On our side, a keyword and blocklist pass answers first, and any URLs in the message are checked against our blocklist. Only a message that pass cannot decide is given to a model — and then through the same path as everything else, described under Where analysis happens, with the same order and the same conditions.
What is stored: no record of the message. This is the one place in this document where that is the answer. Unlike the triage and visit records described below, an SMS check writes none — not the sender, not the body, not the verdict, not the action returned. There is no message history in the portal, and "Delete my scan history" has nothing to delete here because nothing was written.
What is logged, which is the exception to the paragraph above. A request that reaches the endpoint handler produces one line in the same operational log described under The application log, with the same lifetime — which is to say nothing in the application deletes it. That line carries your raw IP address, the app version, a truncated hash of the sender (the first 12 hex characters of an unsalted SHA-256 — the caution stated for triage records applies here identically), whether the message contained a link, which of the two request shapes it arrived in, which pass decided it, the verdict and action, and how long it took. The message body is not logged, and the sender does not appear in the clear.
What we cannot offer you here, stated plainly. Every access and deletion route in this document works from a pseudoId or an extensionId, and an SMS request carries neither. So there is no record to return to you and none to delete — because none was written — and the log line described above is tied to nothing but the IP address it came from. We say "the log line" rather than "the only trace" deliberately: what a model provider does with a message we sent it for analysis is governed by their terms, not by this sentence. If you want the log line cleared, write to privacy@phishtriage.com; we clear it by hand, as with the rest of the log. We would rather say this than imply a remedy that does not exist.
Where analysis happens
This section is new in version 1.5. It exists because the claim it replaces was wrong in a direction that flatters nobody: the policy said your content goes to Anthropic as a matter of course, and that is no longer how the routing works.
The order. Anything that needs a model — a scan you pressed Analyze on, or a text message that got past both passes above — is offered to backends in a fixed order, and the first one to return a usable answer is the one that answers:
- A model we host ourselves. A machine we run, on our own network, rather than a model service we buy. This is asked first.
- A third-party model service. Not in the path in any deployment we run as of this version's effective date. It is named under Third-party processors, which says exactly what would put it there.
- Anthropic's Claude API. The last resort, and the only one of the three with nothing behind it to try, so its failure is not handed on: the request ends without a verdict. Its answer is checked all the same. A malformed optional field is dropped, and an answer that does not match the shape the rest of the system requires is treated as a failure rather than used.
The conditions. A tier is asked only if it is configured, has not been switched off, and the request still has time left in its budget; a tier that is not configured is simply not in the order. A tier that is asked and fails hands on to the next, and "fails" is deliberately broad: unreachable, too slow, an error, a rate limit, output we cannot parse, or a verdict that does not match the shape the rest of the system requires. Any of those, and the next tier gets the same content.
One thing can come before the order, and only where our intelligence hub is in use: a scan whose links, sender domain or attachment digests the hub already holds as malicious, and marked safe to act on automatically, is answered from that and offered to no model at all (Shared threat intelligence, from version 1.7). On the hosted service, as at the effective date at the top of this document, the hub is not in use.
One tier can be asked twice about an email. When a tier ahead of Anthropic — our own model, or the third-party service — calls an email phishing or suspicious, names the sender's own domain name as the brand being impersonated (the "Acme" of acme.com), and none of our deterministic checks found anything wrong with the message, a second request to that same tier is due. It is not due in a few narrower cases: when the sender is on a free-mail or shared-hosting domain; when we cannot state the sender's domain for certain, as when the sender field is long enough that it may have been cut short; or when that domain carries the name of one of a few organisations whose own domains we keep a reviewed list of, and is not a domain we count as theirs (anthropic.co rather than anthropic.com). The second request carries the same content plus a short added instruction that states the sender's domain and asks for a concrete sign of deception or a reconsidered answer. When too little time is left to make it, it is skipped and the first answer is used; otherwise the second answer is used if it is usable, and the first if not. It goes only to the tier that just answered, never onward and never to Anthropic, so it adds no recipient: it is a second copy of the same content to the same one. If that tier is the third-party service, that service receives your content twice.
Why there is no sentence here saying your content stays on our hardware. Because it would be false on exactly the days it matters. The fallback exists because the first tier fails, and when it fails your content goes onward — that is the entire point of having one. In the week of 17 August 2026 a sampling parameter our self-hosted model rejects sent every scan to Anthropic until it was found and corrected. We are not putting a proportion or a duration here: any number we printed would be a measurement of one moment, and the routing is a rule, not a ratio. What you can rely on is the order and the conditions above.
Anthropic remains a processor. Less often is not never. A version of this policy that dropped Anthropic would be wrong the first hour the local machine misbehaved, and it misbehaved that week. Its entry under Third-party processors stands.
We are not telling you which country that machine is in, and that is deliberate. The Estonia statement under Third-party processors is about the PostgreSQL host and has only ever been about that machine. It is not a statement about the model server, and a draft of this section wrongly reused it before review caught it. We will name a location here when we can state it as precisely as we state the database's, and not before.
This page is dated, and the routing is not. Which tiers are configured is a property of the running service and can change without this document changing. So everything above is written as a rule plus a statement of the arrangement as at the effective date at the top — not as a claim about this instant. Where the two could diverge, the rule is the part you can rely on.
What does not change with the routing. Whichever tier answers, the content sent is the content listed above and the same verdict comes back. Analyze scans have the same record stored; SMS checks store no record, as stated under Text messages.
What the record now says about the routing. Since version 1.6 a stored triage record, and the log line written with it, notes which tier answered and, when a tier ahead of it failed first, why: one reason from a fixed list, such as a timeout, an error or output we could not parse. If we had to put back commas missing from the answer we kept before we could read it, which happens only on a tier ahead of Anthropic, the record and the line note how many. When a second request to the tier that answered was due, as described above, they also note how it went (answered, skipped for lack of time, or a reason from the same fixed list), the verdict of the first answer, and whether the verdict kept is a different one. All of these are words from fixed lists or a count, and none carries any of your content. This paragraph used to say that a stored triage record does not note which backend produced the verdict, so we could not tell you afterwards which one analysed a particular scan of yours — and neither could you. The first half is no longer true: an access request under Your rights returns it with the rest of the record. The second half still is, because nothing you can open in the extension or the portal shows it.
What the backend stores, and for how long
The proxy at api.phishtriage.com (Node/Express; its source is not public today) logs every triage request and visit ping to a PostgreSQL database on hardware we own and operate — see Third-party processors below for the full storage picture. Stored fields per record:
- Triage records (
indexTriageinphishtriage-proxy/src/store/postgres/data.js): the sanitized request body (subject, from, body text, etc.), the verdict, your pseudoId, your extensionId if registered, a hashed form of your IP, a cache key, a timestamp, and which tier answered, with the reason a tier ahead of it failed when one did, how many missing commas we put back into its answer before we could read it when we had to and, when a second request to the tier that answered was due (made, or skipped for lack of time), how it went, the first answer's verdict and whether the verdict kept differs. Each record also carries the date it will be deleted (see Retention). This line used to say "the Claude verdict", which stopped being right when the routing changed — the verdict stored is whichever tier answered — and until version 1.6 it went on to say that the record does not say which one that was. It does now (Where analysis happens). On the hash: it is the first 12 hex characters — 48 bits — of an unsalted SHA-256 of the address. This document used to describe it as "SHA-256, not the raw IP" and leave it there. An unsalted hash of an address space as small as IPv4 can be reversed by computing the whole space, so treat this as pseudonymised, not anonymised. It is also not the only place the address appears: see the application log, below. - Visit records (
indexVisit): the URL or hostname, type (url/domain), your pseudoId, extensionId, hashed IP, timestamp, and the date it will be deleted (see Retention). A visit ping beyond the number we record for one device in an hour is still answered, but no record is written for it. - File-check records, written by the file scanner, only with file protection on: the file's SHA-256 fingerprint, its name (up to 300 characters), its size, the verdict, which check gave it, the deep scan's engine or the name of the malware signature it matched when there was one, your pseudoId, your extensionId, a timestamp and the date it will be deleted. See When you opt into File protection. Up to version 1.9 this list did not include them.
- Extension records (
indexExtension): your extensionId, pseudoId, the browser identifier (chrome/edge/firefox/brave), an optional label you provided ("Work Laptop", etc.), when the device registered, and when it was last active — a timestamp we rewrite on every scan and every visit ping we record. The last two were not listed here before version 1.4; the last-active one is live activity, not registration metadata, and it is on neither retention clock. See Retention. - Evidence records (
insertEvidence), unless you switched evidence capture off: the screenshot and HTML described above, a SHA-256 of each so their integrity can be shown later, the verdict of the scan they belong to, the cache key of that scan, your account and device ids, the time we received them, and the capture time your browser claimed — recorded as a claim, because it comes from the machine being defended. - Feedback records (
insertFeedback), one for each press of "Looks safe to me" or "Looks dangerous" since version 1.7: your pseudoId and extensionId, your answer, the verdict you were shown, the time your device gave and the time we received it, which client sent it, whether it was forwarded to the intelligence hub, and a keyed hash of the page's address where the key for it is configured, which on the hosted service, as at the effective date at the top of this document, it is not. Never the address. See Your corrections to a verdict.
Account and identity records
This subsection was new in version 1.2. The record types above had been presented as the whole storage picture, and they are not: they are the activity records. The control plane that sits under them was added when the product grew accounts, teams and plans, and it is where an email address actually lives. In schema order (migrations/ in the proxy repo):
- Accounts — an opaque server-minted account id, your display name, your roles, status, which organisation you belong to and when you joined it, the period you chose for your own scan history, if you chose one, and when, and timestamps.
- Identities — one row per provider login: the issuer, the issuer's subject id for you, your email address, whether the issuer says that address is verified, your Google Workspace domain if you have one, and first- and last-login times. Created the first time you sign in to the portal and not before. The account id is the key everything else joins on; the email address is stored for display and audit and is deliberately not indexed or used as a key.
- Devices — the extension records above, plus an asset tag and who claimed the device, for fleets.
- Tokens — device access and refresh tokens, and device-link codes, stored as hashes with expiry, use and revocation times. Never in the clear.
- Organisations — the org, its name, its reporting mode, its proved email domains and the challenge used to prove each one, its people and their roles, its invitations (which carry the invited email address), its enrolment tokens, and the team's scan-history period, if its owner chose one, and when.
An organisation can take you into it without asking, and until version 1.4 this document never said so. It described two routes into a team — an invitation you accept, and an enrolment token an administrator pushes to a device — and there is a third. If an organisation has proved that it owns an email domain, then the next time someone signs in to the portal with a verified Google address on that domain, that account is made a member of that organisation. There is no invitation, no prompt, and no notice at sign-in; it happens during a sign-in that looks like every other sign-in. From that moment the organisation's owners can see that account's scans and its phishing evidence on the same terms as any other member's — subject to the reporting mode described under Keeping evidence of phishing pages.
Three limits are real and worth stating. An account that is already in an organisation is never moved into another one. Only activity from the instant the organisation switched to per-person reporting is ever attributed, so joining does not retroactively expose what you did before that. And the portal tells you which organisation you are in, when you joined, and which domain put you there, precisely because this is the one membership nobody is asked about. If you would rather it did not happen, do not sign in to the portal with an address on that domain — the extension scans perfectly well without an account.
A domain can be proved two ways, and only one of them is visible to you. The first is a DNS TXT record the organisation publishes. The second identifies the organisation by your identity provider's tenant — Google Workspace's hd claim today — and requires the organisation to publish nothing at all. So checking your domain's DNS records is not a way to rule this out.
Back to the record inventory:
- Subscriptions — plan, status, seat counts, period end, and an opaque reference to the payment provider's record. No card details: see Third-party processors.
The application log
Separately from the database, the proxy writes an operational log line as it handles a request. Six kinds of line matter here. The third was missing from this list until version 1.5, because the surface it belongs to was missing from this document, the fourth arrived with the report buttons in version 1.7, and the last two were added to the service on 3 October 2026 and to this list in version 1.10:
- Triage — written for every scan that reaches the model. It carries the raw client IP address, plus the time, whether the scan was of an email or a page, your pseudoId, a hashed sender address, how many characters the subject had, the verdict, the confidence and risk score, whether the verdict recommends escalating, which tier answered and why a tier ahead of it failed, how many missing commas we put back into the answer of a tier ahead of Anthropic before we could read it, whether a second request to the tier that answered was due (made, or skipped for lack of time) and, if it was, how it went, the first answer's verdict and whether the verdict kept differs, and how long the request took. On a page scan the sender address is the page's hostname and the subject is its title, so the line carries a hash of the hostname and the length of the title. That hash, like the sender hash of an email scan and the IP hash above, is unsalted, and hashing a list of candidates reverses it: next to the raw IP address, a page scan's line all but names the site you scanned. Not the body or the page text, and not the subject, the title or the page's full URL. The time, the email-or-page field, the pseudoId and the escalation flag were on this line before version 1.6 as well, and this list left them out. A scan answered from the one-hour cache logs only the cache key and the time, with no address at all. Where the intelligence hub is in use and answers a scan before any model is asked (Shared threat intelligence), this line is written too, naming no tier, and a second line beside it carries only the time, the cache key, and the kinds of source and indicator the hub's answer rested on.
- Visit — written for each visit ping, if you switched on Background protection or Send full URLs. It carries the time, the hashed IP, the type, and the URL or hostname, which is the same thing the record stores, with whether a record was written and how long the request took. For a ping that is not recorded (see Visit records above) the line carries no IP hash and no URL or hostname: only the time, the type, that it was not recorded, and how long it took. Up to version 1.9 this bullet did not mention pings that are not recorded.
- SMS — written for each text message the iOS filter sends us, if you use it. It carries the raw client IP address, the app version, a hashed sender, whether the message held a link, which pass decided it, the verdict and action, and the duration. Not the message text, and not the sender in the clear. Unlike the two above, this line is not a companion to a stored record — it is the only trace an SMS check leaves anywhere. See Text messages.
- Feedback — written only when a report from the buttons under a verdict arrives with an address we discard, or cannot be stored, plus a note the first time after a restart that a report carries an address and no key for the address hash is configured. It carries the time and the reason and, for a discarded address, which client the report named. Not the address, not your pseudoId and not your IP address. A report that is stored normally writes no line at all. See Your corrections to a verdict.
- Capacity, for one network address — written the first time in an hour that scans from one network address are refused because that address has used its hourly share of the scans we send to a model. It carries the time, a hash of the address — the same unsalted 12-character hash as above, taken for an IPv6 address of the block of addresses it belongs to — the size of the share, and when the hour ends.
- Capacity, for everyone — written the first time in an hour that the service refuses scans because it has reached its hourly ceiling of scans sent to a model. It carries the time, the ceiling and when the hour ends, and the same kind of hash of the network address that sent the most of that hour's scans, with how many.
Other lines are about a device or an account rather than a scan. They carry its extensionId, pseudoId or account id and nothing from your scans: a device that presents a credential we no longer accept, an account whose device credentials we revoke, an account that joins an organisation on a proved domain, a portal action refused for lack of a role, and a device that takes its organisation over the number of seats its plan includes, logged with the seat count and the plan when the device registers or sends a late enrolment token.
The file scanner at filescan.phishtriage.com, a separate service of ours, keeps a log of its own if you use file protection: one line for each request it handles, with the request, its status, how long it took and the first eight characters of the device's extensionId and, for a file check, the verdict, which check gave it, whether a deep scan was offered, the malware signature a deep scan matched, and the first 60 characters of the file's name. Not your IP address and not the file. Up to version 1.9 this document did not mention it.
This is the sense in which "we don't know your IP address", the sentence that stood at the top of this document until version 1.2, was false. The hashing is real, and it is what the database keeps; it is not what the log keeps.
The retention sweep described next deletes rows from the database. It does not touch these logs — nothing in either application deletes them, so their lifetime is whatever the operator's log handling gives them. A deletion request that names your extensionId covers them, including the lines that carry only the pseudoId or account id that extensionId belongs to; we clear them by hand. An SMS line has no extensionId in it to name — Text messages says what that means for a request. Nor has a capacity line, which names a hashed address and nothing else about anyone.
Retention
Evidence records are the exception to everything in this section: they are kept on the separate, per-verdict clock set out above, and the scan-history sweep does not touch them. Because they can outlive the scan they came from, they have their own list in the portal — see Who can look at it above — so a capture stays reachable, and individually deletable, for as long as it is kept. Unless your organisation reports in aggregate, which is the mode every organisation starts in: there the list is withheld from everyone and the whole-history button below is the only route.
Triage records, visit records and file-check records are deleted automatically. Each is given its deletion date when it is written, from the period in force at that moment for the account it is written under:
- The default. On the hosted service it is 365 days for records written since we switched to it on the effective date at the top of this document. Before that it was 90 days.
- A team's period. A team's owner can choose a period from 7 to 365 days in the portal. It applies to every member of the team, and to every device the team set up.
- Your own period. Anyone signed in to the portal can choose a period for their own records there, from 7 to 365 days. In a team you can choose the team's period or a shorter one, never a longer one, and if the owner later sets a period shorter than yours, the team's applies to your new records. This covers scans from devices linked to your account. Devices your organisation set up for you follow the team's setting. Team owners are not shown the period a member chooses.
Changing a period changes the deletion date of records written after the change only: a shorter period does not delete older records early, and a longer one does not keep them longer. So a scan or visit record written before the switch to 365 days keeps the 90 days it was given. File-check records had no deletion date until we began giving every record one, shortly before version 1.10 took effect: those made before then were given 365 days from when each was made, and those made between then and the switch to 365 days were given 90. A sweep removes every record past its deletion date several times a day, so a record can outlast its date by a few hours.
The reports you send with the buttons under a verdict do not follow these periods: the same sweep deletes each one 90 days after we receive it, whatever period applies to your scans.
When you or a team's owner change a period, we keep a record of who changed it, from what to what, and when, and for a change to your own period, the team's period at that moment. It holds no scan content, and neither the sweep nor "Delete my scan history" removes it.
The default is configured server-side (RETENTION_DAYS); self-hosted proxies choose their own, or set none — in which case records with no chosen period are kept until deleted by hand, and this policy does not apply to that deployment. Up to version 1.9 this section described one retention window of 90 days for every triage record, visit record and report, and it did not mention file-check records, which nothing deleted.
Independent of the sweep:
- You can delete your own history yourself, immediately. The portal dashboard has a "Delete my scan history" action that removes every triage and visit record, every file-check record, and every report from the buttons under a verdict, stored under your account's identity, at any time, without waiting for its deletion date. Where our intelligence hub is in use it also asks the hub to erase everything filed under your pseudonyms there, and shows you what the hub answered, with the receipt's identifier when the erasure is confirmed — see Shared threat intelligence. As at the effective date at the top of this document the hub is not in use on the hosted service, so there is nothing there to erase and no receipt is shown. It also removes every evidence capture stored under your account — including captures whose scan record has already expired, which are the ones the per-verdict clock above keeps longest. Deletion is immediate and cannot be undone. It removes your records only — teammates' histories are theirs, and no organisation role can delete them for you. This used to say it removed every capture made on your own devices; it is scoped to your account, and the edge two bullets down is the difference between those two things.
- Or one capture at a time. The Phishing Evidence list on the dashboard deletes a single screenshot and its page source, leaving the rest of your history alone. Inside a team that is an owner's action, and it exists only where the organisation reports attributed — an aggregate-mode organisation withholds the list from everyone, so there the whole-history button above is the only route. That button is yours either way.
- What neither of these reaches: your device record. The sweep deletes triage, visit and file-check rows and reports, and evidence runs on the per-verdict clock above. The record of the device itself — its extensionId, the browser, the label, when it registered and when it was last active — is on neither clock and has no expiry at all. "Delete my scan history" does not remove it: it removes your scans, your visits, your file checks, your reports and your captures, and leaves the device inventory the portal's Devices list is drawn from. To have a device record itself deleted, write to
privacy@phishtriage.comwith its extensionId — the same route as for the application log. - One honest edge: records a device made before you linked it to your account were written under the device's own prior identity and stay there — linking deliberately does not rewrite history (that rule is what stops a link from claiming someone else's past records, in either direction). Those records are not shown in your dashboard, are not reachable from the delete action, and are deleted by the sweep on the date each was given when it was written. If you want them gone sooner, use the email route below with the device's extensionId.
- The triage cache lookup only considers records younger than one hour, so old records never affect verdicts.
- If the extension has discarded its identity (Sign out or Leave team), records made under the old identity can no longer be reached from any device or account — the extension cannot name an identity it no longer holds, and identities are minted by the server, never accepted from a device. Those orphaned records are deleted by the sweep on the date each was given, like everything else.
- You can still request deletion by writing to
privacy@phishtriage.comwith your extensionId — useful when you no longer have the browser profile the extension was installed in. The popup shows that ID under ⚙ Settings → Advanced, a section that stays closed until you open it, as "Device ID", but only its first eight characters; send us those and tell us which browser and roughly when you installed, and we will find the record. This document used to say the ID was "visible in the popup under ⚙ Settings" without saying it was abbreviated there, which made a remedy we offer look easier than it is, and up to version 1.9 it did not say the ID was under Advanced.
Pseudonymous ID
Your install is identified by a random string we call the "pseudoId". It is stored in chrome.storage.local under the key pseudoId and is sent with every triage and visit request. Depending on whether you have registered, it is one of two things:
- Before registration — a 12-character random hexadecimal string the extension generates on your device the first time it runs, using
crypto.getRandomValues(for example,a1b2c3d4e5f6). The backend does not treat this as proof of anything: requests carrying only a locally-generated ID are recorded as unregistered and are not attributed to any account or portal dashboard. - After registration — a 32-character random string (128 bits) generated by our server and sent back to your device, which stores it in place of the local one.
Neither form is derived from your email, IP address, hardware ID, fingerprint, or any other identifier. Both are fresh random numbers. We treat the ID as a correlation key, not as proof of who you are: it exists so repeat use from the same install can be linked together server-side (for caching, or to show you a per-device history in the portal), not to identify you as a person.
When registration happens, corrected. This section used to say "registration is an explicit action; it does not happen on install". The opposite is the case, and has been since registration stopped being a button: it runs automatically from the browser's install event, and again before the first scan of a session if that attempt did not succeed. There is no register control in the popup. So on a working network the device-generated ID above exists only until the first /register call returns, and every install holds a server-minted identity before you have done anything with the extension.
Registration is not a sign-up and involves no account of yours. What it sends is the current pseudoId, which browser you are on, an empty label, and — only on a fleet where an administrator has pushed one by policy — an enrolment token that attaches the device to that organisation. No email address, and nothing you type. It fails silently and retries later: an unregistered device still scans.
From extension version 1.1.0 the extension also sends an enrolment token, and nothing else, to /devices/enrol, with the device's own credential, whenever it holds one the backend has not yet given a definite answer about. That includes a token an administrator pushes to a device that is already installed, or changes; a token that registration carried but that did not join the device to an organisation; and, once, a token already set by policy when a device updates to version 1.1.0. It sends the same token again only until the backend has given it a definite answer: after a request that went unanswered — the backend busy, down or out of reach — it waits, for longer after each failure, and tries again. What it keeps about that on your device is listed under What the extension keeps on your own device.
We previously derived a registered user's identifier from a hash of their email address. We no longer do, and the derivation has been removed: a value computed from an email address is not genuinely pseudonymous, because anyone holding the address can recompute it.
The popup's account row can discard that identity. This document used to call the control Unregister, which is not a word in the interface: the button reads Leave team when your device belongs to an organisation and Sign out when you are signed in on your own, and it appears only in those two cases. Either one clears extensionId, pseudoId, the organisation and the stored tokens, and from extension version 1.1.0 the enrolment record too — and then immediately registers again, so what you end up with is a fresh server-minted identity rather than none. From the backend's point of view you are a different install; your old records stay where they are and can no longer be reached from this device.
When one ID covers several devices, corrected. This paragraph used to say that linking to a team "via an invite code" overwrote your pseudoId with the team owner's, and that this was the only way one ID reached more than one device. Every part of that is now wrong. There is no invite-code field in the extension any more — typing a code there enrolled a machine as a team device with no person attached, so it was removed, and a device now joins a team from the portal, where an administrator can see what is being attached to whom.
What actually happens: when you sign in, or when an administrator links your device from the portal, your local pseudoId is replaced by that account's id. It is never an organisation id — those are a separate namespace — and where an administrator links a device from the portal, the id it takes is the id of the account they linked it to, which may not be yours. The popup re-reads it from the server each time you open it. So every device you sign in on carries the same id and requests from all of them are linked together server-side; that is what makes one history out of several devices, and it is as true of a personal sign-in as of a team one. An install that has never been signed in keeps an id unique to itself.
Shared threat intelligence
This section was added in version 1.7, and it describes a destination for your data that no version before 1.7 mentioned. It is written before the feature is switched on rather than after, which is the only order that makes the disclosure worth anything.
We run an internal intelligence hub: a service of ours that collects indicators — link URLs, domains, hosts, file digests — from our own scanning and from partners, so that a page one customer reports can be recognised instantly for everybody else. It is not a third party. It is also not this proxy, and that is the point of this section: it is a third destination, alongside the two hosts named under Third-party processors, and data derived from your scans can reach it.
Every flow described below is off in the shipped configuration, and as at the effective date at the top of this document every one is off on the hosted service. Where one is on, this is exactly what happens. Up to version 1.9 this paragraph said each would stay off on the hosted service until a dated revision of this document said otherwise. From version 1.10 one may be switched on without a further revision, so this section is written to be true whether it is on or off. Every statement in this document that the hub, a flow to it or its configuration is not in use on the hosted service is a statement as at the effective date, not a promise that it still is.
What leaves, and what never does
Only values derived from a scan, never its content:
- link URLs, including the path — with the query string removed, and with the path itself removed when any part of it looks like a per-recipient token (a long random-looking segment, a uuid, or anything that decodes to an email address). Where the path is removed, the host is still sent.
- the registrable domain and the host of each of those links.
- the sender's domain — the part after the
@, never the address. - digests of attachments, where a client sends them. No shipped client does today.
- addresses and domains the model itself named as indicators in its verdict. An address goes only in the same cut-down form as a link URL, and not at all where its path would be removed.
- the verdict band the model reached, as a low-confidence claim marked as coming from a model.
Asking before a model is asked. The proxy can also ask the hub about those same link URLs, domains, hosts, sender domain and digests before a model reads the message, and waits a fraction of a second for the answer. If the hub already holds one of them as malicious and marked safe to act on automatically, that answer becomes the verdict and no model is asked; the triage record and its log line then name no tier as having answered it. Any other answer, or none in time, changes nothing, and the scan goes to a model as it would have.
A capture an owner confirms. When an organisation owner confirms a phishing capture (Keeping evidence of phishing pages), the page's address, cut down the same way, and a digest of its HTML are sent as a confirmed finding about that page, and a later rejection of the capture withdraws it. Where the address's path would be removed, nothing is sent. A capture of a mail message has no page address, so confirming one sends nothing. The screenshot and the HTML themselves are never sent. The finding carries no pseudonym, and it is sent in the proxy's own name, not the owner's. Like everything the proxy sends to the hub, it is filed under the organisation, with the time (see How long the hub keeps it).
What never leaves the proxy for the hub: the message body, the subject line, the HTML, the raw headers, the recipient, the sender's address, attachment filenames, screenshots, page source, and anything you typed. The same list is enforced in code and asserted by a test that serialises a contribution built from a message stuffed with each of those and searches the bytes for them.
The pseudonym, and what it is scoped to
A contribution carries a subject reference: a keyed hash that stands in for your account, computed as HMAC(HMAC(secret, organisation), account). Three consequences follow, and they are the reason it is not a plain hash:
- the hub can act on the reference and can never reverse it, because the key never leaves our proxy.
- the same account in two organisations produces two different references, so the hub cannot join your activity across them.
- an account with no organisation contributes nothing at all. Solo accounts have no tenant, and the proxy refuses to ask the hub about, or tell it about, anything derived from a scan it cannot scope. That refusal covers the pre-analysis lookup as well as the contribution.
Your corrections, where forwarding is on
What we keep of a press of "Looks safe to me" or "Looks dangerous" is set out under Your corrections to a verdict, and that part does not depend on the hub. With report forwarding on, each report is also sent to the hub as a report about the page: your answer, the page's address cut down to its scheme, host and path (none at all for a report made on mail, or where the path looks like a per-recipient token), the time your device gave (ours, if it gave none), which kind of client sent it, a random identifier for the report itself, and the pseudonym above, filed under your organisation — nothing else about you, and nothing at all for an account with no organisation. It goes as a low-weight report, marked as not grounds on its own for blocking the page, and the pseudonym on it follows the report's own 90-day clock (below).
How long the hub keeps it, and what deletion reaches
The hub keeps what it is given with no time limit, and lets go of the pseudonym that links it to you when our record does. What it keeps is what an indicator is: the link URL, domain, host or digest itself, how often it was seen, the claims made about it and the verdict reached, and, for each sighting, forwarded report and confirmed finding the proxy sends, which organisation it came from and when. What it does not keep past our own record is the pseudonym that links an observation to you. Each contribution tells the hub how many days are left before the record it came from is deleted here — the scan, for a contribution from a scan, under the period described under Retention; the report, for a forwarded report, on the reports' 90-day clock — and when that time is up the hub removes the pseudonym and keeps the rest. That happens within a day of the date our record is deleted, and later only if the hub's own worker is not running. A contribution made from a record already past its date carries no pseudonym at all.
What the hub keeps can still point to you. The organisation and the time stay with no time limit. In an organisation with one member, the organisation identifies that member. In any organisation, we could match the time of a sighting against our application log, whose triage line carries your pseudoId and the time of each scan, for as long as that log is kept (see The application log). And once the hub has let go of the pseudonym on a sighting, "Delete my scan history" no longer reaches it, because nothing on it names you any more.
This is worth stating plainly because it is the one place where our own retention sweep has no reach: the sweep deletes rows in our database and cannot delete a row in the hub's. Up to version 1.9 this section said contributions carried an expiry we set, equal to your scan-history period. The hub keeps the indicator instead, and what ends with the period is the pseudonym.
"Delete my scan history" reaches the hub too. When you delete your history, the proxy asks the hub to erase everything recorded under your pseudonyms — one request per organisation you have ever contributed under, including ones you have since left — and the hub returns a receipt; we keep its identifier and a hash of it with our record of the deletion. The portal shows you which of three things happened, under the result of the deletion: the erasure was confirmed (with the receipt's identifier), there was nothing there to erase, or it could not be confirmed just now. The third is not a failure of your deletion: your records here are gone either way, and that sentence means the shared copy has not been acknowledged yet. Nothing is filed at the hub under your pseudonyms — no contribution and no forwarded report — unless this erasure is switched on as well.
Two honest limits. An indicator that was already aggregated into the hub's own judgement about a URL — "several people have reported this page" — does not disappear when the join to you does; what is erased is the link between you and the observation, and the hub's receipt says so in its own words. And a claim the hub has already distributed to a downstream consumer cannot be unpublished by deleting the row that produced it.
What the file scanner may send
The file scanner at filescan.phishtriage.com may also be connected to the hub. As at the effective date at the top of this document it is not. Where it is, it sends, for each file its deep scan found to be malware, including files checked before it was connected and before version 1.10 took effect, for as long as the record of the check is kept: the file's SHA-256 fingerprint, its size, the name of the malware signature it matched, when the file was checked, and a reference to its own record of that check. Never the file, its name, your account, your device or a pseudonym. The hub drops the reference to our record when that record's deletion date comes, under the period described under Retention, and keeps the fingerprint and the verdict. A file whose record is already past its date is not sent. Each is sent as a claim weighted so that one detection is enough for the hub to judge the fingerprint malicious, and a fingerprint the hub judges malicious can be passed on to others who use the hub's lists, which is what the contribution is for.
The blocklist, which travels the other way
The proxy can also pull a blocklist from the hub, which is data coming to us rather than data leaving. Nothing about you is sent to fetch it: it is a signed list of hostnames and URLs, requested with no parameter derived from any user's activity. It is listed here for completeness rather than because it discloses anything.
Two more pulls are like it. Where the file scanner is connected to the hub, it pulls the hub's signed list of fingerprints of known malware, and a file whose fingerprint is on it is called malicious without the file being sent. And from extension version 1.1.0, where an administrator's policy names a hub and the keys that sign its lists (intelHubUrl and intelRootJwks), the extension, with file protection on, asks that hub for the download blocklist when a download needs it, and asks our proxy only when the hub gives it nothing. After a pull that gives it a list, it does not ask the hub again for an hour. Otherwise, when the hub's list is empty or the last pull failed or was refused, it asks again on the next download, and so on every download until a pull gives it a list. The request carries nothing about you, but it goes from your browser straight to the hub, so the hub sees your network address and, since each request follows a download, when you download, though not what.
Third-party processors
Anthropic — the Claude API, and the last tier of the order set out under Where analysis happens. Content reaches it when the tiers in front of it do not answer, which is a condition and not a frequency. We are deliberately not printing a proportion here: it would be a measurement of one moment presented as a property of the service, and the misconfiguration described under Where analysis happens is what that looks like when it goes the other way. This paragraph used to open "when you press Analyze, the extracted content is forwarded by the proxy to Anthropic's Claude API for analysis" — unconditional, correct about Anthropic receiving content, and wrong about the routing. Anthropic acts as a processor on our behalf under its API terms; per those terms, Anthropic does not use inputs to API requests to train models. See anthropic.com/legal/privacy for their data-handling commitments.
Ollama — a hosted third-party model service (Ollama Cloud), named here before it is used rather than after. It is the middle tier of that same order, and a tier is in the path only when it is configured, which is the rule stated under Where analysis happens. As of this version's effective date it is configured in no deployment we run. That is a statement as at the effective date above, not a permanent one — the rule that survives is the one in the previous sentence: a tier is in the path only when it is configured.
We are naming an unused processor for two reasons, and we think the alternative is worse. Turning it on takes no new code and no release — three configuration values and a restart — so the distance between "not in the path" and "in the path" is minutes, while the distance between editing this document and it reaching you runs through a store review. And this disclosure is the thing that enabling it is meant to wait for: the deploy configuration says in as many words that this policy must name Ollama before that tier carries real traffic. A policy that has to be rewritten the day someone sets three environment variables is a trap, so we would rather over-name a processor than under-name one. If it does take traffic, what it receives is what the tier ahead of it would have received — the content described under What the extension collects, and when and under Text messages — and nothing additional.
Records are stored in a PostgreSQL database on hardware we own and operate, located in Estonia. That sentence is about the database host and only about it; see Where analysis happens for why this document does not tell you where the model server is. Since production switched to PostgreSQL in August 2026, no third-party storage provider has kept the records described under What the backend stores — there is no managed-database provider in the path. Cloudflare, Inc. carries traffic between your browser and our server (TLS termination, tunneling, DDoS protection) and is a processor for data in transit only; it does not store your records.
Up to version 1.7 this section described one legacy exception: records created before the August 2026 migration to our own hardware, the pre-release records of the beta period, stayed in our previous datastore at Elastic Cloud (Elasticsearch as a managed service, europe-west3 GCP region, under Elastic's DPA), outside the automatic 90-day deletion. We deleted that Elastic Cloud project on 2 October 2026, and we kept no export, snapshot or copy of it. What Elastic itself retains of a deleted project, such as its own backups, and for how long, is governed by its terms and its data processing agreement with us; we send it nothing any more. Records created since production switched to PostgreSQL in August 2026 were never stored there.
This section used to end "we use no other third-party processors". Anthropic, Cloudflare and Elastic were the whole list when it did. Two more arrived with accounts and billing, and both are reachable from the popup's Log in button:
- Stripe — payments, and only if you buy a plan. We create the checkout session and pass it the email address you signed in with; Stripe holds the card, the invoices, the retries and its own customer portal. We store an opaque reference to the subscription and the plan state, and we never see a card number. Nothing about you reaches Stripe unless you start a checkout.
- Google — portal sign-in, and every portal page load. This bullet used to say "only if you sign in", and that is not what the portal does: every page of it loads Google's sign-in library from
accounts.google.comand its fonts fromfonts.googleapis.comandfonts.gstatic.com, before you have signed in and whether or not you ever do. Opening the portal therefore tells Google your IP address, your browser and that you were on our site. Nothing about your scans goes to Google — those never leave our server for them. Sign-in itself is still not a processor relationship: you authenticate on Google's own pages, so Google knows you signed in to PhishTriage, and Google is the source of the identity claims listed under Account and identity records rather than a place we send data to. We also receive Google's Cross-Account Protection notices, which is how a Google account that has been hijacked or disabled loses its portal session here instead of staying valid for a day.
Beyond those: no analytics, no advertising, no telemetry SDKs, no error-reporting services, no fingerprinting libraries. That is a statement about what we have built into the product, not a claim that no third party is ever contacted — the portal's Google dependencies above are, on every page.
Three destinations receive your data, all three ours:
api.phishtriage.com— the triage proxy. Everything described above goes here: analysis, visit checks if you turned them on, evidence, your corrections to a verdict, and your account. The iOS message filter posts to a different path on this same host — see Text messages. What this host then does with content is the order described under Where analysis happens; "ours" is where your content arrives, not a promise about where it stops.filescan.phishtriage.com— the file scanner, and only if you turned file protection on. It receives what the file-protection section describes: a fingerprint of a file by default, and the file itself only when you ask for a specific one to be checked, or on every download if you switched on "Deep scan every file" (from extension version 1.1.0, except a file a page built and saved by itself). It keeps the record of each check that section describes. With file protection off — which is how it ships — this host is never contacted.- the intelligence hub — an internal service of ours, reached by the proxy and, where we connect it, by the file scanner. Your browser does not reach it unless, from extension version 1.1.0, an administrator's policy points the extension at it for the download blocklist. It receives values derived from a scan, and your corrections to a verdict where forwarding them is on, and only where an organisation is resolved and the corresponding flag is on, each filed under that organisation, and, where the file scanner is connected, fingerprints of files its deep scan found to be malware. Every flow to it is off in the shipped configuration and, as at the effective date at the top of this document, off on the hosted service. Up to version 1.9 this entry said it would stay off there until a dated revision of this document said otherwise. Shared threat intelligence above sets out exactly what leaves, under what pseudonym, for how long, and what your deletion reaches there. It is named here because this list used to say "two hosts, both ours" and read as a closed list — which is the kind of sentence that has to be corrected before the thing it excludes is switched on, not after.
All three are operated by us on the hardware described above; none is a third party. An organization's admin policy can point the first two at a self-hosted service instead, in which case that service is your organization's to describe, not ours.
One further request, which the old wording denied. This list used to be introduced as "the extension talks to two hosts, both ours, and to nothing else". That has not been true since file protection shipped. With file protection on, an ordinary download has to be fetched a second time to be inspected at all — the browser does not hand an extension the bytes of one — and that second fetch goes to the address the download itself came from, which can be any host on the internet, carrying your cookies for that site so that a file behind a login can still be read. What the site sees is a second request for the file it just served you: the request carries no header, identifier or parameter of ours, and says nothing about your other browsing. The bytes go no further than your browser's memory unless the file-protection section above says otherwise. With file protection off, it does not happen.
Self-hosting
Organizations can run their own proxy and point their fleet at it by pushing an admin policy via enterprise managed storage (see the managed-storage.json schema in the source). This is deliberately the only way to change the backend: there is no user-facing setting, so nobody can be talked into pasting an attacker's URL and routing their scanned email to it. When your organization self-hosts, the rest of this document still describes the extension's behavior, but the backend's behavior is governed by your operator's policy, not ours. Your IT team is the data controller for those records. We have no access to a self-hosted proxy's data.
Your rights
You have, depending on jurisdiction, the right to:
- Access the data we hold associated with your pseudoId or extensionId. Email
privacy@phishtriage.comwith the ID; we'll return the records. - Delete that data. Same email, same IDs. Both of these routes work from an ID, and a text-message check carries none — there is no record of one to return or to erase, and its log line is reachable only by IP. See Text messages.
- Withdraw consent for Background protection. Switch it off in the popup — and Send full URLs under Advanced, if you turned that on — or remove the underlying permission in your browser:
chrome://extensions→ PhishTriage → Details → Permissions on Chrome, Edge and Brave, orabout:addons→ PhishTriage → Permissions on Firefox. This entry namedchrome://extensionsalone, in a document that is submitted to Mozilla and covers a Firefox build; that page does not exist there. Removing the permission does stop the visit checks at once — the browser tells the extension, and it detaches its navigation listener there and then. The permission that does this iswebNavigation: removing only the access to all sites does not stop the hostnames or addresses being sent. It does not reset the switches. This entry used to promise that "the toggles in the popup revert to off the next time you open it", and they do not: the popup draws them from your saved settings without re-checking the permission, so they will still look on, with nothing behind them. The same goes for the file-protection switch after you removedownloads. Switching them off in the popup is what actually clears the setting, so if you want the interface to match reality, do that as well as, or instead of, revoking. And on a managed fleet none of this is yours to withdraw: an administrator's policy beats the popup, so switching a forced setting off changes nothing about what is sent. The popup marks such a control Managed and will not let you move it. Removing thewebNavigationpermission still stops the reporting there and then, policy or no policy, because the browser tells the extension and it detaches its navigation listener — but the setting is your administrator's to change, and from extension version 1.1.0 the popup asks you to allow the permission again. - Unlink from a team account, or sign out. The popup's account row shows Leave team if your device belongs to an organisation and Sign out if you are signed in on your own; both clear the local identity, as described under Pseudonymous ID. This document used to say "click Unregister", which is not a control that exists. A device that has registered but never signed in shows no button there at all — use the email route above with its extensionId.
- Object to processing. Email
privacy@phishtriage.com. - Complain to your data protection authority (in the EU/UK).
We will respond to requests within 30 days. If you've used the extension across multiple browsers without linking them, each install has a separate pseudoId and you may need to request deletion for each.
Changes to this policy
When we make a material change — a new collection, a new processor, a shorter or longer retention period — we will bump the Version and Effective date at the top of this document and, where reasonable, display a notice on the next-launched welcome page. The full revision history of this document is kept in git; the repository is not public today, so write to privacy@phishtriage.com for a prior version and we will send it. This paragraph used to link the file directly, which returned 404 to everybody.
Version 1.10, and how you were told of it. Version 1.10 is a material change. It lengthens a retention period: on the hosted service, scan, visit and file-check records written after the switch to the new default are kept 365 days, where those written before it keep the period they were given, and you or your team's owner can now choose a shorter period, down to 7 days. It describes, for the first time, records we were already keeping: the file-check records the file scanner has written since file protection shipped, which nothing deleted, and the file scanner's own log. It adds the record we keep of each change to a retention period, and the two capacity lines our application log has carried since 3 October 2026. It changes what the intelligence hub keeps: indicators, with the organisation each came from and when, with no time limit, and the pseudonym that links them to you only as long as our record of it. It says what the file scanner would send the hub if it were connected, including checks made before then. It lets a flow to the hub be switched on without a further revision of this document, which this document said up to version 1.9 would not happen. And it describes what extension version 1.1.0 changes from version 1.0.0, and corrects several sentences in place, each naming the version it corrects.
We did not show a notice on the extension's welcome page for version 1.10: no extension release carries one. The portal's dashboard shows a one-time notice instead, once the retention choice is switched on: "You can now choose how long we keep your scans, from 7 days to 1 year." It is shown to people who sign in to the portal and can choose a period of up to a year, or who set their team's period. A member of a team that keeps scans for less than a year is not shown it, and the notice does not mention the new default or any other change. If you do not sign in to the portal, nothing but this document tells you of version 1.10.
Contact
privacy@phishtriage.com
For security disclosures (vulnerabilities in the extension or proxy), write to security@phishtriage.com.