Security & architecture
Send to Case is a Chrome extension that uploads files from your browser to matters in your Clio account. This page is written for IT managers and security reviewers: what we receive, what we don't, where your OAuth token lives, what it can do, and how to revoke it.
The one-paragraph summary
Files never touch our infrastructure. They are fetched by the extension's own offscreen document
inside your browser, staged in a browser-private sandbox, and uploaded directly to Clio's S3-backed
document storage over HTTPS. We receive two things: the short-lived OAuth handshake (which we proxy
but do not persist) and scrubbed error events from Sentry (no file names, file contents, matter names,
or client names). Your Clio access and refresh tokens live only in your browser's
chrome.storage.local, on your device.
Files go straight to Clio
Every upload follows one path: the extension fetches and stages the bytes in your browser, then PUTs them directly to Clio's S3 storage. Our proxy only brokers the OAuth handshake — it is never in the file path.
Two things reach us: the proxied OAuth handshake and scrubbed Sentry errors. File contents are never among them.
How a file actually moves
Every upload — file picker, right-click link, batch scan, or captured portal download — follows the same path. No files transit our servers, and no data is staged outside your browser.
-
Fetch. The extension's offscreen document (a sandboxed, extension-owned page
Chrome runs in the background) fetches the file from its source URL. Redirects are validated one hop
at a time and blocked if they target private IPs,
localhost, or link-local addresses, so court-site links that pass through internal hosts stay safe. - Stage. Bytes are written to OPFS (Origin Private File System), a browser-managed sandbox scoped to the extension. OPFS storage is isolated from the web page, from other extensions, and from your regular file system.
- Upload. The extension asks Clio's API for an S3 presigned upload URL, then PUTs the staged bytes straight to Clio's storage. Large files (>100 MB) use S3 multipart upload from the same offscreen context.
- Clean up. OPFS staging is deleted when the upload completes (or is cancelled).
Our Cloudflare Worker is involved only in the OAuth handshake and account/entitlement calls. It is not in the file path and never sees file contents, file names, or the S3 PUT.
What data we receive
1. The OAuth handshake (in transit only)
When you click "Connect to Clio" and again when your access token expires, the extension sends the
authorization code or refresh token to our proxy at api.sendtocase.com, which adds our
Clio client secret and forwards the request to Clio. Clio responds with access and refresh tokens,
which our proxy returns to the extension. The proxy is a Cloudflare Worker and does not write tokens
to any database, cache, or log.
2. Scrubbed error events
When something breaks, the extension and backend emit error events to Sentry so we can diagnose it. Those events include:
- Stack trace and error message
- Extension version, browser, and operating system
- Size bucket (e.g. "small", "medium", "large") — not exact bytes
- MIME type (e.g.
application/pdf) - A short hash of the file name (for correlating related errors)
Before an event leaves your browser, a scrubber removes email addresses, file paths, query strings, and common PII patterns. File names are hashed in code before they are ever attached to a log or event.
3. Account and usage counters
If you create a paid account, we store an account row (Clio user ID, email, plan status) and a monthly upload counter used to enforce plan limits. We store the count, not what you uploaded or where. Payment details are handled by Stripe — they never reach our servers.
What data we do not receive
- Files or file contents. The extension uploads bytes directly from your browser to the S3 URL that Clio's API returns. Our servers are not in that path.
- Matter contents, matter names, or client names. We send matter IDs to Clio's API on your behalf; we do not store or log the names.
- Court records, case data, or document metadata beyond what the browser needs to render the page you started from.
- Your Clio password. You enter it on Clio's site during OAuth. We never see it.
- Browsing history. The extension does not declare any persistent content scripts and never watches your tabs, reads URLs, or records navigation. Code only runs on a page when you explicitly invoke it — for example, choosing "Find downloadable files" from the right-click menu, which temporarily injects a one-shot scanner via
chrome.scripting.executeScriptto find downloadable links on the current page. The scan happens in your browser; the list of found links is not sent to us.
What we receive — and what we never do
The full ledger a reviewer checks first. Green is what reaches us; oxblood is what never can, by architecture.
We receive
3 things- +OAuth handshakeProxied to Clio in transit — never written to a database, cache, or log.
- +Scrubbed error eventsStack trace, version, size bucket, MIME type, a hashed file name.
- +Account & usage countersClio user ID, email, plan status, monthly upload count (paid accounts).
We never receive
by design- ✕Files or file contentsUploaded browser → S3 directly; our servers aren't in that path.
- ✕Matter or client namesWe pass matter IDs to Clio; the names aren't stored or logged.
- ✕Your Clio passwordEntered on Clio's site during OAuth. We never see it.
- ✕Browsing historyNo persistent content scripts; code runs only when you invoke it.
Before an event leaves your browser, a scrubber removes email addresses, file paths, query strings, and common PII patterns. File names are hashed in code before they are ever attached to a log or event.
The left column is everything we touch. Files, names, and passwords sit only in the right column — never received.
The downloads permission, in plain terms
Send to Case uses Chrome's downloads permission for two things:
- Performing uploads. When you pick a file from a right-click or batch flow, the extension may ask Chrome to download it into the offscreen upload pipeline described above.
-
Capturing a portal download (opt-in, per click). Some sites use buttons that trigger
downloads through in-page code (
javascript:/blob:/ fragment links) that a Chrome extension cannot fetch directly. If you right-click one of those and choose "Send to Case", we open a small confirmation window and watch only your next download from that tab, for up to two minutes, so we can match it to your click and upload it. Matching is scoped to the tab and time window you opened; anything you download outside that scope is ignored. Extension-initiated downloads are filtered out so we can never "match" our own activity.
Send to Case does not scan, index, or enumerate your existing download history beyond fetching recent entries when you open the "Recent downloads" panel in the popup — and even that list stays in the browser.
Where your OAuth token lives
Your Clio access and refresh tokens are stored in chrome.storage.local, the
browser-managed keystore scoped to the extension on your device. They are:
- Not synced to other devices and not stored in
chrome.storage.sync. - Not written to any server we operate — our proxy holds them only for the milliseconds of the token exchange.
- Readable only by the Send to Case extension on the Chrome profile that installed it.
- Cleared when you disconnect from Clio, uninstall the extension, or clear extension storage in Chrome.
The OAuth exchange uses PKCE (RFC 7636). The one-time code verifier lives in chrome.storage.session
for the length of the sign-in flow and is deleted as soon as the code is exchanged.
Your token lives on your device
Clio access and refresh tokens are kept in the browser's own keystore, scoped to the extension. They never land on a server we operate.
- ✓Not synced to other devices; never in
chrome.storage.sync. - ✓Never written to a server we operate — the proxy holds them only for the milliseconds of the exchange.
- ✓Readable only by the Send to Case extension on the profile that installed it.
- ✓Cleared when you disconnect, uninstall, or clear extension storage in Chrome.
The OAuth exchange uses PKCE (RFC 7636). The one-time code verifier lives in chrome.storage.session for the length of the sign-in flow and is deleted as soon as the code is exchanged.
The proxy is a pass-through: it holds a token for the milliseconds of the exchange, then forgets it. Persistence is on your device only.
What the token can do
Clio's OAuth does not expose fine-grained scopes for API access: an access token inherits the permissions of the Clio user who approved it. In other words, the token can do what that user can do in Clio — no more, no less. In practice, Send to Case uses the token for three kinds of calls:
- Matters (read): search and list matters so you can pick one.
- Documents (create): create document records and obtain the S3 upload URL your browser writes to.
- User profile (read): confirm the signed-in Clio user so the popup shows the right account.
If your firm restricts a user's access inside Clio (for example, to a subset of matters), the token inherits those restrictions automatically.
The token inherits your permissions
Clio's OAuth has no fine-grained scopes — an access token can do whatever the Clio user who approved it can do, no more. In practice the extension makes three kinds of calls.
The scope labels are illustrative groupings, not literal Clio scope strings — Clio has none. Restrict a user inside Clio and the token inherits it automatically.
How to revoke access
You can cut off the extension's access to Clio in three ways:
- From Clio (recommended for firm-wide revocation). In Clio, go to Account Settings → Apps & Integrations → Connected Apps, find "Send to Case", and click Revoke. This invalidates the refresh token immediately, so the extension cannot mint new access tokens even if a user forgets to sign out.
-
From the extension. Open the Send to Case popup and click Disconnect. This
deletes the tokens from
chrome.storage.localon that device. - By uninstalling the extension. Removing the extension from Chrome deletes its local storage, including the tokens. To also invalidate the refresh token server-side, follow step 1 as well.
Three ways to cut off access
Revoking from Clio is the strongest — it invalidates the refresh token server-side, so the extension can't mint new access tokens even if a user forgets to sign out.
chrome.storage.local on that device.Steps 2 and 3 clear the device; only step 1 kills the refresh token on Clio's side.
Infrastructure at a glance
- Extension: Chrome Manifest V3. Three in-browser surfaces — a service worker (orchestration, messaging), a popup (UI), and an offscreen document (the upload worker). The extension does not declare any persistent content scripts. Open a Chrome Web Store listing's "View source" to inspect the shipped bundle.
- Backend proxy: A single Cloudflare Worker at
api.sendtocase.com. It handles the Clio token exchange, Stripe webhooks, and low-volume account endpoints. It does not see or proxy file contents. - Database: Cloudflare D1 (SQLite at the edge) for account rows, subscription status, and monthly usage counters.
- Error monitoring: Sentry, with a scrubbing pipeline that strips PII before transmit.
- Payments: Stripe Checkout and billing portal. Card data is handled by Stripe; we receive only subscription metadata.
Reporting a security issue
If you believe you have found a security vulnerability, please email [email protected] with details (steps to reproduce, the affected component, and any proof of concept). We will acknowledge your report within two business days.
Coordinated disclosure. We ask that you give us a reasonable opportunity to investigate and remediate before any public disclosure — typically up to 90 days from our acknowledgement. We will not pursue legal action against researchers who report in good faith, avoid privacy violations and service disruption, and do not access or modify data that is not their own.
Remediation timelines. Once we have triaged and confirmed a vulnerability, we target the following timeframes to ship a fix, prioritised by severity:
- Critical (data exposure, account takeover) — within 7 days.
- High — within 30 days.
- Medium — within 60 days.
- Low — addressed in a future scheduled release.
We will keep you updated on our progress and let you know when a fix has shipped.
Related documents
- Privacy Policy — the full list of data categories and third parties.
- Terms of Service
- Our listing in the Clio App Directory — the same product, reviewed from Clio’s side.