Overview
Account discovery scans your connected Gmail inbox to find every service you have ever signed up for. It searches for welcome emails, verification emails, password reset notifications, and sign-in alerts to build a comprehensive list of your online accounts. Discovered accounts are matched against your vault to show which services are already stored and which are missing — giving you a complete picture of your digital footprint.How it works
Gmail search queries
PassAgent runs seven targeted Gmail searches designed to catch the common email patterns services send during account creation and use:| Query | What it catches |
|---|---|
subject:("welcome to" OR "thanks for signing up" OR "thanks for joining") | Onboarding emails |
subject:("verify your email" OR "confirm your email" OR "email verification") | Email verification |
subject:("your account" OR "account created" OR "account confirmation") | Account creation |
subject:("get started" OR "getting started") | Activation emails |
subject:("password reset" OR "reset your password" OR "change your password") | Password reset |
subject:("new sign-in" OR "security alert" OR "login notification") | Security alerts |
subject:("subscription" OR "your plan" OR "your order" OR "order confirmation") | Commerce |
format: "metadata" for efficiency — full message bodies are never read.
PassAgent uses read-only Gmail API scopes. It reads only the From and Date headers of matching messages. It does not read email bodies, attachments, or any messages that do not match the discovery queries.
Domain extraction
TheextractDomainFromEmail() function parses the From header to extract the sender’s domain, then strips common email infrastructure subdomains to get the actual service domain:
Stripped prefixes: mail., email., mailer., noreply., notify., notifications., accounts., account., info., news., newsletter., support., help., no-reply., updates., marketing., transactional., bounce., e., em., m., mg., post., comms.
For example, noreply@email.spotify.com becomes spotify.com.
Filtered domains
Email infrastructure providers and personal email services are excluded from results since they represent the email platform, not a service account:Full skip list (31 domains)
Full skip list (31 domains)
gmail.com, google.com, googlemail.com, outlook.com, hotmail.com, live.com, microsoft.com, yahoo.com, aol.com, icloud.com, me.com, mac.com, mail.com, protonmail.com, proton.me, mailchimp.com, sendgrid.net, sendgrid.com, amazonses.com, mandrillapp.com, mailgun.org, mailgun.com, postmarkapp.com, sparkpostmail.com, mailjet.com, sendinblue.com, brevo.com, constantcontact.com, campaignmonitor.comService enrichment
Each discovered domain is matched against PassAgent’s known services database usingfindServiceByQuery(). Matched services receive:
| Field | Source | Example |
|---|---|---|
serviceName | Known services DB or domain capitalization | ”Spotify” |
serviceIcon | Favicon URL from getServiceFavicon() | https://spotify.com/favicon.ico |
serviceColor | Brand color from known services DB | #1DB954 |
category | getServiceCategory() | ”Entertainment” |
loginUrl | Known login page URL | https://accounts.spotify.com/login |
acme.com becomes “Acme”) with a generic gray color.
Vault cross-reference
Discovered accounts are matched against your existing vault entries by comparing domains. A match is found if:- The vault entry’s
websiteorurlfield resolves to the same domain - The vault entry’s
namecontains the service name (case-insensitive) - The vault entry’s website/URL contains the discovered domain as a substring
inVault: true with a reference to the vault entry’s vaultPasswordId.
Sign-in discovery enrichment
The unified/api/accounts endpoint goes further by detecting which accounts use OAuth (“Sign in with Google”) instead of passwords:
Match connected emails to vault entries
For each connected email integration (Gmail, Outlook, Yahoo), PassAgent checks which vault entries use that email as the username.
Detect passwordless accounts
Vault entries where the username matches a connected email AND the password field is empty (or contains only whitespace/placeholder text) are flagged as likely OAuth sign-ins.
Response format
The discovery response includes both the account list and summary statistics:| Stat | Description |
|---|---|
total | Total discovered accounts |
inVault | Accounts with a matching vault entry |
notInVault | Accounts missing from the vault |
oauthLikely | Accounts that appear to use OAuth (no password) |
dismissed | Accounts the user has dismissed |
Auto-import to vault
ThePOST /api/accounts/add-to-vault endpoint bulk-imports discovered accounts as placeholder vault entries:
Select accounts to import
The user selects one or more discovered accounts (by domain) from the UI. Up to 200 accounts can be imported in a single request.
Check for duplicates
The server fetches existing vault entries and checks if any share a domain with the accounts being imported. Duplicates are skipped.
Create placeholder entries
For each new account, a vault entry is created with the service name, domain, URL (
https://{domain}), and the first connected email as the username. The password field is left empty and encryption_version is set to -1 (placeholder — the client must encrypt with the vault key).Caching and persistence
Discovery results are persisted in thediscovered_accounts table (auto-created if missing). Subsequent GET requests return cached results without re-scanning Gmail. The POST endpoint triggers a fresh scan and upserts results, preserving the user’s dismissed state for previously seen accounts. Stale entries (accounts from a previous scan that no longer appear) are automatically removed.
Dismiss and manage
Users can dismiss irrelevant accounts viaPATCH /api/accounts or PATCH /api/discover-accounts with { id, dismissed: true }. Dismissed accounts are hidden from the default view but remain in the database and can be un-dismissed at any time.
Rate limiting
| Endpoint | Limit | Window |
|---|---|---|
POST /api/discover-accounts (scan) | 5 requests | 1 hour |
POST /api/accounts (scan) | 30 requests | 60 seconds |
PATCH /api/accounts (dismiss) | 60 requests | 60 seconds |
POST /api/accounts/add-to-vault | 30 requests | 60 seconds |
Token management
Gmail OAuth tokens are automatically refreshed before each scan. If a token refresh fails, the integration is marked as disconnected and the user is prompted to reconnect. A Redis lock (gmail_refresh_lock:{userId}) prevents concurrent token refresh races when multiple requests arrive simultaneously.
API endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/discover-accounts | Return cached discovered accounts |
POST | /api/discover-accounts | Trigger a fresh Gmail scan |
PATCH | /api/discover-accounts | Dismiss or un-dismiss an account |
GET | /api/accounts | Return discovered accounts with sign-in enrichment |
POST | /api/accounts | Trigger a scan with optional email filter |
PATCH | /api/accounts | Dismiss/un-dismiss or mark accounts in vault |
POST | /api/accounts/add-to-vault | Bulk-import discovered accounts as vault placeholders |
The
/api/accounts endpoint is the newer, unified version that supports multiple connected email providers and OAuth sign-in detection. The /api/discover-accounts endpoint is the original implementation that supports Gmail only. Both are functional; the unified endpoint is recommended for new integrations.