A free script that filters unknown senders out of your inbox and lets you charge them to get through. Paid mail auto-delivers to your inbox with a star. Optional AI classifier for edge cases. Set up in 10 minutes. Runs forever on Google's servers.
Four layers. Use any combination.
Every 15 minutes, each new sender is scored against your contacts, thread history, sender reputation, bulk-mail headers, calendar invites, role addresses, and transactional subject patterns. Three strictness presets — Strict, Balanced, Loose — decide the cutoff. Clearly-not-a-person mail (info@, order receipts, shipping, calendar invites) passes through automatically.
Newsletters with unsubscribe headers and mail from 80+ common services (Amazon, FedEx, LinkedIn, your bank) pass straight through. The script also learns from sender-domain history — mail from a domain you've seen repeatedly gets trust over time, even if the sender isn't in your contacts.
When a filtered sender pays your Stripe link, a webhook moves their message back to your inbox with a ⭐ star and adds them to a permanent allowlist. No manual dashboard-checking — the receipt reaches you automatically.
For messages right on the edge, the script can call Claude Haiku (bring your own Anthropic API key) to decide: real person or automated mail? Roughly $0.0001 per grey-zone email. Fully optional — flip the toggle in the configurator to enable.
12 steps, ~10 minutes. No coding knowledge needed.
Skip if you only want the filter without the pay-to-reach feature.
https://buy.stripe.com/abc123...).Open the configurator. Pick a strictness preset (Balanced is a good default), paste your Stripe link, add your name and any trusted domains, then click Download customized script. This saves a gmail-gatekeeper.js file with your settings already baked in.
Go to script.google.com and click New Project. Make sure you're signed into the Google account whose Gmail you want to filter.
Select all the default code (Cmd+A or Ctrl+A), delete it, and paste in the entire contents of your downloaded gmail-gatekeeper.js. Hit Cmd+S / Ctrl+S to save. Name the project whatever you like — "Gmail Gatekeeper" is fine.
In the left sidebar, click the + next to Services. Add People API (lets the script read your contacts) and Gmail API (lets it detect your send-as aliases so auto-replies come from the right address).
In the toolbar, select setup from the function dropdown and click ▶ Run. Google will ask you to authorize — click through the permissions flow.
Click the ⏰ clock icon in the left sidebar. You should see a trigger for processNewMessages, set to run every 15 minutes. If it's there, the filter is live.
Skip if you don't want paid mail to auto-deliver back to your inbox.
In Apps Script, select getWebhookSetup from the function dropdown and run it. Open the Executions tab (left sidebar) to see the log — it prints a URL like:
https://script.google.com/macros/s/AKfycbx.../exec?t=RANDOM_TOKEN
Copy that whole URL. Then:
checkout.session.completed.Test it: in the Stripe webhook's page, click Send test webhook → checkout.session.completed. Then check the Executions tab in Apps Script — you should see the request come through.
Skip if you didn't enable the AI fallback in the configurator.
ANTHROPIC_API_KEY. Value: your key (starts with sk-ant-). Click Save.isPerson: true for the cold-outreach sample and isPerson: false for the no-reply sample.Before flipping auto-reply on for everyone:
Happy with the test? In the configurator, toggle Send auto-reply with Stripe link on, download the script again, and paste the new version into Apps Script (saving overwrites the old config). Every filtered sender now gets the auto-reply. The filter itself runs whether auto-reply is on or not.
Every 15 minutes, each recent sender earns "pass points" from a set of signals. If the total clears the strictness threshold, the message passes through untouched. If not, it's labeled Unknown Senders and archived out of your inbox. The label shows unread counts so you always know when something's there.
Signals and points:
List-Unsubscribe, List-Id, Feedback-ID, Precedence: bulk, Auto-Submitted) — i.e., a newsletter or automated mailtext/calendar or .ics attachment)info@, noreply@, billing@, orders@, etc.)Thresholds: Strict needs 50 points to pass, Balanced needs 30, Loose needs 15.
Every decision logs exactly which signals fired and the resulting score in the Apps Script Executions tab — so when something gets filtered that shouldn't, you can see why.
When enabled, any message with a score between 10 and the threshold (i.e., some safe signals but not enough to pass) gets sent to Claude Haiku with a prompt asking: is this a real person trying to initiate contact, or automated mail? The model returns structured JSON with a verdict, confidence, and reason. Results are cached per-sender so the same conversation never gets classified twice.
Typical cost is roughly $0.0001 per grey-zone email — most users spend well under $1/month. Requires an Anthropic API key stored in Script Properties (never in the script itself).
The script ships with 80+ pre-configured domains for services most people get email from. It matches subdomains automatically — so amazon.com in the list also covers email.amazon.com, shipment-tracking.amazon.com, and any other subdomain:
You can add or remove domains from the COMMON_SERVICES array in the script at any time.
When enabled, the script sends a single reply to each unknown sender with your Stripe link. It's smart about it:
You can fully customize the auto-reply by editing AUTO_REPLY_SUBJECT and AUTO_REPLY_BODY near the top of the script. Change the wording, the tone, the price — whatever you want. The ${STRIPE_LINK} variable automatically inserts your payment link. Use ${YOUR_NAME} to insert your name. Test any changes by running testAutoReply to send it to a friend before going live.
The $5 default is just a suggestion. You can set any amount in Stripe and frame it however fits your style:
Tweak the auto-reply text to match whatever you choose. If you're donating the proceeds, say so — it makes the whole thing feel less like a toll booth and more like a feature.
If you deployed the script as a web app and added the Stripe webhook (steps 8 and 9), paid mail auto-delivers. Stripe fires a checkout.session.completed event the moment a payment succeeds; the webhook hits your script, which looks up the payer's email in the Unknown Senders label and moves the matching thread back to your inbox with a ⭐ star. The sender also gets added to a permanent allowlist so they never get filtered again.
End-to-end delivery is usually within a few seconds of payment.
If you didn't set up the webhook, delivery is manual: check your Stripe dashboard, see who paid, and search the Unknown Senders label for their address to rescue the thread yourself.
If a real person gets filtered, just add them to your Google Contacts. Next time the script runs, they'll pass right through. In Gmail, you can hover over a sender's name and click "Add to contacts" — takes two seconds. For an entire domain, add it to WHITELISTED_DOMAINS or COMMON_SERVICES.
The script defaults to running every 15 minutes, which is the sweet spot for most people. You can change this by editing the setup function — look for .everyMinutes(15) and change it to .everyMinutes(5) or .everyMinutes(10). After editing, run setup again to replace the old trigger.
Don't go lower than every 5 minutes. Google Apps Script has a daily execution quota — 90 minutes for regular Gmail accounts, 6 hours for Google Workspace. Each run hits both the Gmail and People API, so at very high frequencies you'll burn through the quota and the script will stop running partway through the day. At 15 minutes (96 runs/day), you're well within limits. At 5 minutes (288 runs/day), you're still fine but closer to the edge.
Core filtering runs entirely on Google's servers — no data leaves unless you opt in to the AI fallback or Stripe webhook.
| Permission | What it does | Risk |
|---|---|---|
| Gmail (full) | Search inbox, add/remove labels, archive, star, send replies | Medium |
| People API | Read-only access to your contacts and other-contacts | Low |
| Gmail API | Read your send-as aliases (read-only) | Low |
| Script triggers | Create the 15-minute recurring timer | Low |
| Web app deploy | Expose the Stripe webhook endpoint — only if auto-deliver is used | Low |
| External fetch | Call Claude Haiku API — only if AI fallback is enabled | Low |
Most issues happen during setup. Here's what to look for.
This is normal for personal Apps Scripts. Click Advanced at the bottom left, then click "Go to [project name] (unsafe)". Google shows this because you haven't submitted the script for their app review process, which is only needed for public apps.
Usually a multi-account session issue. Your browser is confused about which Google account to authorize. Fix: open an incognito window, sign into only the Google account you want, go to script.google.com, and try again from there.
You haven't added the People API as a service. In the left sidebar, click the + next to Services, find People API, and click Add. Then run the function again.
The script didn't get pasted in correctly. Go back to the code editor, select all (Cmd+A), delete, and paste the full script again. Save. The dropdown should now show setup, testRun, testAutoReply, and others.
Make sure you selected setup from the function dropdown before clicking Run. If you ran a different function, the trigger won't be created. Select setup, run it, then check the Triggers tab (clock icon in the left sidebar).
If you're logged into multiple Google accounts, the OAuth flow can pick the wrong one. The developer email shown doesn't affect functionality — the script runs under whichever account owns the Apps Script project. If it bothers you, use an incognito window with only one account signed in.
The sender isn't in your Google Contacts or "Other Contacts." Add them to your contacts and they'll be whitelisted on the next run. If it's an entire domain (like a company), add it to the WHITELISTED_DOMAINS or COMMON_SERVICES array in the script.
The newsletter probably doesn't include a List-Unsubscribe header (some small newsletters don't). Options: add the sender's domain to the Trusted domains list in the configurator, or lower the strictness preset. The Executions log will show exactly which signals did and didn't fire for that sender.
Reply to them once — Gmail will automatically add their address to "Other Contacts," which the filter treats as a strong pass signal. Or add their specific address to Trusted email addresses in the configurator. Or have them pay the Stripe link — they'll get permanently allowlisted.
The URL you pasted into Stripe is missing the ?t=RANDOM_TOKEN query string. Run getWebhookSetup in Apps Script and copy the full URL it prints — the token is what authenticates Stripe to your script.
Expected for test events — Stripe's test payload uses a dummy email ([email protected]) that won't match anything in your Unknown Senders label. To verify end-to-end, have a real friend pay $1 through your Payment Link using an address that's currently filtered. The thread should reappear in your inbox within seconds.
Open Apps Script → ⚙ Project Settings (gear icon in left sidebar) → scroll to Script Properties. Add a property named exactly ANTHROPIC_API_KEY with your key as the value. Keys start with sk-ant-. Save, then run testAIClassifier to confirm it works.
No. Config changes (toggles, strictness, whitelists, auto-reply text) only need a Save. Redeploying the web app is only needed if you want a new URL, which you almost never do.
Check a few things: (1) Is AUTO_REPLY set to true? (2) Has the script already replied to that sender before? Each sender only gets one reply. (3) Is it a no-reply address or newsletter? The script skips those. (4) Check the Executions tab (left sidebar) for error logs.
If you're on Google Workspace (not regular Gmail), your admin may need to allow internal apps. Go to admin.google.com → Security → Access and data control → API controls → Settings. Make sure "Internal apps" is set to "Trust internal apps" and "Unconfigured third-party apps" is set to "Allow."
Select resetRepliedSenders from the function dropdown and run it. This clears the list of senders who've already received an auto-reply, so the next time they email, they'll get one again.
Yes. Google Apps Script runs on Google's servers, not your computer. Once the trigger is set, it runs 24/7 regardless of whether your computer is on, off, or disconnected.
Yes. The script runs server-side on Gmail before your client syncs. Labels show up as folders in IMAP clients. You may need to subscribe to the "Unknown Senders" folder in your client's settings for it to appear in the sidebar.