Legal
OneOff Capture — Privacy Policy
Last updated: October 6, 2026
Applies to: the OneOff Capture Chrome extension, version 2.0.1 and later
This policy covers the OneOff Capture browser extension only. The privacy policy for the OneOff website and mobile apps is here.
Who This Is For
OneOff Capture is an internal tool. It is distributed privately through the Chrome Web Store, visible only to the OneOff administrators named as its testers, and is used by that handful of administrators to build the OneOff product catalog. It is not a consumer product, and it has no users other than those administrators.
What the Extension Reads
The extension reads only the pages it is explicitly told to read. It declares no content scripts, so it runs nothing at all while you browse normally. A page is read in exactly three situations:
- You open a product page and press Capture this page in the extension’s popup. The page in the active tab is read.
- The OneOff admin panel at
https://admin.oneoff.worlddispatches a list of URLs to the extension, or you paste a list into the popup’s batch box. Each URL is opened in its own minimized background window (or, while your browser window is full screen, an inactive background tab in it), read, and closed. - You switch on queue worker mode in the extension’s options (it is off unless you do). The extension then asks the OneOff API every 30 seconds for queued URLs, at
/creator/products/resolve-from-client/capture-jobs/claim, and reads each URL it is given exactly as in the second case. Switching the mode off stops this.
There is no list of approved retailers inside the extension. It holds permission for every https site, and it will render and capture any https URL it is given — that is deliberate, because which retailers OneOff carries changes constantly and cannot be fixed in a manifest. The only limits the extension itself applies are that plain-HTTP pages, browser-internal pages (chrome://, file://) and unparseable URLs are refused before anything is read.
What Is Read from a Page, and What Is Sent
Once a page is judged to contain a settled product description, the extension takes a copy of the document and removes everything it does not need: every <script> that is not JSON-LD product metadata, along with <style>, <svg>, <noscript>, preload links and HTML comments. What remains — the page’s markup and its product metadata — is sent to the OneOff API, together with the URL requested, the URL the browser actually ended on if a redirect occurred, and the capture token described below.
Because the page is rendered in your own logged-in browser, the captured markup is whatever that session sees. On a retailer where you happen to be signed in, or which personalises by region, the markup can therefore include session- or account-specific content — your name, a saved address, a basket or wishlist, loyalty details — and the extension does not attempt to strip it. Captured HTML can therefore contain personal data about the administrator who captured it. No such data is sought, and the OneOff API extracts only product fields from the markup, but it is present in the bytes that are sent, and this policy does not claim otherwise. See What Is and Is Not Collected below.
Requests to the retailer come from your own IP address and use your existing browser session, exactly as if you had opened the page yourself. The extension does not read, store or transmit browser cookies itself; it holds no cookies permission. The OneOff API sees the IP address the capture is sent from, which it uses to rate-limit the endpoint.
Only the page you named is read. No other tab, no browsing history, no bookmarks, no keystrokes and no mouse activity are accessed at any point. Form input is not monitored either, though values a retailer has already prefilled into the page are part of the captured markup described above.
Where Data Goes
Captured HTML is sent by HTTPS POST to a single OneOff API endpoint, /creator/products/resolve-from-client, on the environment selected in the extension’s options (production, staging, or a local development server). One further request exists: when a capture dispatched as part of a batch fails terminally, the extension posts a short report to /creator/products/resolve-from-client/failed so the server can release the queued row rather than wait out its 24-hour age-out. That report carries the capture token, the internal identifier of the queued row, and a truncated failure reason — never page content, and never a page URL.
Two more requests exist only while queue worker mode is switched on in the extension’s options (it is off unless you turn it on). In that mode the extension asks the same OneOff API, at /creator/products/resolve-from-client/capture-jobs/claim, for URLs to capture, sending the capture token, an identifier for this installation and how many it can take; it then captures those URLs exactly as above. When it gives up on one of them it reports so to /creator/products/resolve-from-client/capture-jobs/failed, with the capture token, the job’s identifier and a truncated failure reason, never page content and never a page URL. These four requests, all to the OneOff API, are the only outbound requests the extension makes. There is no third-party analytics or error-reporting service, no advertising network, and no remotely hosted code of any kind.
Third Parties That Receive Captured Data
The OneOff API is not the end of the journey, and this section was checked against the API’s source rather than against intent.
Octogen receives the full captured HTML. The API’s first extraction attempt posts the entire submitted document to Octogen’s resolve-from-html endpoint, which returns structured product fields. This is the normal path, not a fallback: it runs on every capture that reaches the API. Octogen describes that endpoint as stateless — its own index and cache neither read nor written — and OneOff’s integration is built on that description, but OneOff cannot independently verify what Octogen retains. Octogen’s own privacy terms govern what happens to the HTML once it arrives.
Nothing else receives the HTML itself. The following receive data derived from it, and each only in the circumstance named:
| Third party | What it receives | When |
|---|---|---|
| Octogen | The full page HTML, and the page URL | Every capture |
Shopify (Global Catalog; the retailer’s own products.json) | The product page URL and its host | Only when Octogen returns no product |
| Google (Vertex AI / Gemini) | The extracted product text, for search embeddings; and the product images themselves, which are sent to a Gemini model to judge and sometimes regenerate the product shot | When a capture creates a new catalog product |
| PhotoRoom | The product image, for background removal | When a capture creates a new catalog product |
| Affiliate networks — CJ, Impact, Partnerize, Awin, Ascend | The product page URL, to mint a tracking link | When the retailer has an affiliate programme configured. Rakuten links are built locally and send nothing |
| Trigger.dev | Background-job payloads: internal record identifiers, not page content | Whenever a capture queues follow-up work |
| Supabase | Hosts OneOff’s database and image storage, so it holds everything OneOff stores | Always |
| Heroku | Hosts the OneOff API, so the request transits it | Always |
Product images are copied from the retailer into a public Supabase storage bucket, so a product image extracted from a captured page becomes publicly reachable at a OneOff URL.
None of these are advertising or analytics services, and the extension itself contacts none of them — every one is reached by the OneOff API, after the capture. No data is sold or rented, and nothing is shared with anyone outside the processors listed above.
What Is Stored, and Where
Everything the extension stores is kept locally in chrome.storage.local on your own machine. chrome.storage.sync is never used, so none of it is uploaded to your Google account or shared between devices. These things are stored:
| Stored | Why | Cleared when |
|---|---|---|
| The capture token and the selected API environment; when the admin panel provided the token, also which admin panel address provided it and when the token expires | Authenticates capture requests to the OneOff API; the expiry lets the admin panel renew the token before it lapses | Overwritten when a new token is saved, provided by the admin panel, or dispatched; removed with the extension |
| Whether batch captures render in visible windows (a developer setting) | Lets the developer choose to watch batch renders for comparison | Changed in the options page; removed with the extension |
| Whether queue worker mode is on, an identifier generated for this installation, and when the extension last asked for queued URLs, how many it has taken and why the last request failed | Lets the extension keep asking for work while the mode is on, and lets the options page show its state | Changed in the options page; removed with the extension |
| The outcome of the most recent single capture — the URL, whether it succeeded, timing figures, and the product title, brand and price the API returned | The popup that requested it is destroyed as soon as it loses focus, so the result has to outlive it | Overwritten by the next capture; removed with the extension |
| The batch queue: each URL, its status, attempt count and timings | Chrome shuts the extension’s background worker down while a run is waiting, so the queue must survive on disk for the run to resume | Cleared by the popup’s Clear button; removed with the extension |
The capture token is held in plain text in chrome.storage.local — Chrome offers extensions no encrypted store — and is sent in the body of each capture request rather than in a header, over HTTPS. The body is used because it keeps the POST a CORS “simple request”, which a header would not. Anyone who can read the extension’s local storage, or who obtains the token from the admin panel, can capture as that account until the token expires.
The token reaches the extension in one of two ways. Normally the OneOff admin panel at https://admin.oneoff.world, open in the same browser while you are signed in to it, provides the token to the extension automatically, together with which OneOff environment it belongs to and when it expires. That handover happens inside the browser, between the admin panel page and the extension; the extension makes no request to obtain it, and the admin panel only replaces the token when it is missing, for a different environment, pasted by hand (so of unknown expiry), or close to expiry. Alternatively you can paste a token from the admin panel into the extension’s options page. Neither way adds an outbound request or sends any additional data: the extension still contacts only the OneOff API endpoints described above.
Captured page HTML is never written to storage. It is passed straight from the page into the request body and discarded when the request completes.
Removing the extension from Chrome deletes all of the above.
Retention
Locally stored data is retained only as described in the table above.
Server-side retention is described here rather than deferred to an unnamed policy, because the extension’s users are entitled to know it:
- The captured HTML is not stored. The OneOff API holds it in memory for the request, forwards it to Octogen, and discards it. No database column and no storage bucket holds page markup, and the API’s logs record only its size in bytes, never its content. What Octogen retains is Octogen’s to state.
- The product fields extracted from it are kept indefinitely. A row in
raw_productscarries the product’s title, brand, description, price, images and URL, together with Octogen’s structured response and the OneOff account id of whoever captured it. Nothing deletes these rows; there is no expiry. Theproductsrow built from them, and the product images copied into OneOff’s public storage bucket, are likewise kept indefinitely. - The queue row for each dispatched URL lives in
creator_pending_productsand holds the URL and its status, not page content. A scheduled sweep deletes it 24 hours after the capture lands. A capture that never completes is marked failed after 24 hours if it was dispatched to the extension, or 7 days otherwise, and a failed row is not deleted — it remains until someone resolves or discards it. - One telemetry row per resolve records the URL with its query string and fragment stripped, which rung answered, and how long it took. It is append-only and is not pruned.
The Capture Token
The token is valid for 60 days from when it is minted. It is a signed, self-contained token with no server-side record, so generating a new token in the admin panel does not invalidate the old one — the old token keeps working until it expires.
A token believed to be compromised is revoked per user: OneOff records a cutoff timestamp against that administrator’s account, and every capture token issued to them before it stops being accepted, usually within thirty seconds. Nobody else’s tokens are affected. The older remedy — rotating the API’s signing secret, which invalidates every capture token for every user at once — is still available and is reserved for the case where the signing secret itself has leaked.
What Is and Is Not Collected
Nothing below is sought, extracted or stored: no health information, no payment information, no location data, no personal communications, no browsing or search history, and no record of user activity such as clicks or keystrokes. The extension contains no tracking, profiling or advertising code, and nothing it handles is used for any purpose other than building the OneOff product catalog. It is not used to determine creditworthiness or for lending purposes.
Two things are collected and should be named plainly rather than left to that list:
- The capture token, an API credential identifying a OneOff account.
- Page content — the markup of each page captured. Because that page is rendered in the administrator’s own signed-in browser, it can incidentally contain personal data about that administrator, as described above. OneOff does not extract, index or store any of it: only product fields are read out and kept. But it is transmitted to the OneOff API and on to Octogen, and it would be wrong to claim no personal data ever leaves the browser.
The honest summary is that the extension collects no personal data about anyone other than the administrator using it, and collects none of that deliberately — but a captured page from a signed-in session is not guaranteed to be free of it.
The use of information received from OneOff Capture adheres to the Chrome Web Store User Data Policy, including the Limited Use requirements.
Children
The extension is an internal business tool. It is not directed at children and is not available to anyone other than the OneOff administrators it is distributed to.
Changes to This Policy
Material changes will be reflected here and the date at the top updated. The version of this policy that applies is the one published at the URL given in the Chrome Web Store listing.
Contact
Questions about this policy, or requests relating to data handled by the extension: adam@oneoff.world.