Files your whole study's email into a searchable knowledge base automatically: a scheduled script reads your Outlook study folder and turns every new message into a note + rebuilds a searchable index. You set it up once; after that it runs hands-off. No Power Automate flow, no SharePoint clicking, no Copilot.
This is the no-GUI equivalent of the old Copilot/Outlook wiki's "when a new email arrives in the study folder → file it" Power Automate flow. The difference: the trigger is a scheduled local script, not a cloud flow. Point it at your study mail once (an Outlook rule into a folder, or a subject/sender filter) and it files the entire study automatically — there is no flow to build, no SharePoint pages to click, and no AI Builder/Copilot credits. Dragging individual emails is only an ad-hoc fallback for the odd message that lives outside the study folder.
A real Windows PowerShell session (no mockups): the actual script code, creating the folder, the ad-hoc drop into _Inbox, the ingest run, the sanitize before/after, the EntryID watermark skipping duplicates, and the one-click macro — narrated, captioned, ops-only. Download · captions (.vtt)
A scheduled run reads your Outlook study folder and files every new message itself — no per-email clicking. It remembers what it's filed, so it never double-files and only ever reads your mail. Runs while your Outlook is open and you're signed in; if it's closed it just catches up next run — nothing's lost.
Update-StudyKB.ps1 reads Outlook directly, writes one clean HTML note per message, and rebuilds the index — no flow builder, no SharePoint clicks, no Copilot.
One index.html you search instantly; an on-device assistant (no cloud) can answer questions over it. Operations-only — never protected health information (PHI).
A sandboxed simulation of the automatic loop. Your inbound Outlook rule decides what enters the CP-101 folder; the scheduled job reads that folder and files the whole batch at once — not one at a time. Watch the red item in the Inbox — a restricted lab-portal email your rule excepts by source: it stays put while the ops mail files itself. Press ▶ Run scheduled job, then add more mail and run again.
idle — press ▶ Run to simulate the next 15-min run
Illustrative only — it mimics the flow; it never runs PowerShell or touches a mailbox. The job files everything in the CP-101 folder — it doesn't read content for sensitivity. The control is your inbound rule, and it routes by source: the ops contacts land in the folder; the lab-portal item (a sensitive channel) is excepted by the rule, so it stays in the Inbox and is never filed. A rule can't classify content — it's a coarse filter, backed by ops-only policy and your review.
Update-StudyKB.ps1Notes\*.html + index.htmlOn each run the script connects to your already-open classic Outlook, reads the study folder(s) you named (or sweeps the Inbox by subject/sender), and files every message it hasn't filed before. It remembers what it's done in .state\seen.json (Outlook EntryIDs), so re-runs are idempotent and your mailbox is only ever read — nothing is moved, deleted, or marked. (EntryIDs are per-store, so mail you later move to an archive/PST can re-file as a harmless duplicate note — never data loss; the seen.json set is the idempotence key, so keep it.) OneDrive does the SharePoint upload, so there's no Graph/connector/API.
About ten minutes, once — then it runs itself. The screens below match classic Outlook for Windows; your version may look slightly different, but the option names are the same. You'll do three things:
① put the script in the synced folder · ② make one Outlook rule so study mail lands in a folder · ③ schedule the script. Step ② is the only fiddly part, so it's drawn out screen by screen.
You'll customize just four things. Decide them now and reuse them throughout (fill the middle column in — this page prints, too):
| What | Your value | Example |
|---|---|---|
| ① Your synced library folder | C:\Users\you\Contoso\Study Knowledge Base - Documents | |
| ② The study — folder name & tag | CP-101 | |
| ③ Your operational senders / distros | CRO PM distro · Data Management distro · named ops contacts | |
| ④ How often it runs | every 15 minutes |
Wherever the steps below show CP-101 or C:\Users\you\Study Knowledge Base, use your values from this table.
Quick-start checklist — click each as you finish it (full details for every line are below):
Update-StudyKB.ps1 into your synced library folderCP-101 folder in Outlook (right-click Inbox → New Folder)CP-101, except sensitive channels / subjectsschtasks command[StudyKB] root = … then + Notes\… linesindex.html in that folder — your searchable wikiUpdate-StudyKB.ps1 into your synced library folder.In File Explorer, open the local folder of your synced SharePoint/OneDrive library (right-click the library — it's already a normal folder on disk). Drop Update-StudyKB.ps1 in there. That folder becomes the knowledge base automatically — there is no path to edit inside the script. Every run prints [StudyKB] root = … so you can see exactly where it's writing.
This keeps the knowledge base fed automatically and keeps participant data out — you route mail by who sent it into a folder the script reads.
CP-101 → press Enter.CP-101. → Next.unblinded, randomization, SAE narrative). → Next.CP-101 → KB), make sure Turn on this rule is ticked → Finish.From now on, operational email lands in the CP-101 folder; sensitive channels stay in your Inbox. Shared mailbox or a deeper folder? When you schedule the script (next), pass the path, e.g. -StudyFolders "Project Mailbox\Inbox\CP-101"; separate several with ;.
No folder rule possible? You can instead point the script straight at your Inbox by sender: add -SenderContains "cro-bioanalytics" (a known ops distro/domain) when you schedule it. Prefer that over -SubjectContains "CP-101" — a subject tag also matches a participant email about CP-101. This path has no exceptions, so use it only when study mail comes from a clean, known sender.
schtasks /Create /TN "StudyKB Update" /SC MINUTE /MO 15 /IT /F ^
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\Users\you\Study Knowledge Base\Update-StudyKB.ps1\" -StudyFolders CP-101"
Change C:\Users\you\Study Knowledge Base to your real folder from step 1. /IT = "interactive" — it runs on your desktop next to your open Outlook. Don't run it as SYSTEM or a stored-password task (there's no Outlook in that session, so it can't attach).
For IT (preferred posture): code-sign the script or allowlist its path; -ExecutionPolicy Bypass above is the unsigned quick-start — ExecutionPolicy is an execution-convenience setting, not a security boundary (per Microsoft). The auditable controls are the signed/allowlisted path and the read-only Outlook access.
powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Users\you\Study Knowledge Base\Update-StudyKB.ps1" -StudyFolders CP-101
On the very first run you may get Outlook's "a program is trying to access Outlook" prompt — it only reads; click Allow. After this you don't touch it — while Outlook is open the scheduled job files new study mail on its own.
On new Outlook or a Mac? The automatic rule path needs classic Outlook. There, drag study emails into the _Inbox folder instead — see Ad-hoc capture and the README.
Almost everything files itself through the rule above. This is only for a one-off that never reaches the study folder — e.g. a thread someone forwards you from outside the study. Operations content only — never drop a participant-level, unblinded, or sponsor-restricted email in here. You put the email in the _Inbox folder and the next scheduled run files it. Two ways — Way 1 needs no setup.
_Inbox folder (no setup · recommended)_Inbox folder in File Explorer (it's inside your synced library — created on the first run)._Inbox folder. That's it..msg; new Outlook / Mac drop a .eml (also handled — see Fidelity for the one-time MimeKit.dll step for full .eml formatting). You can also paste in a .txt/.html.After filing, the original drop is moved to _Inbox\_processed (kept, not deleted) — so if the email "disappears" from _Inbox, that's success, not a failure.
FileToKB.vba is the small macro.FileToKB.vba. Set the INBOX line to your _Inbox path._Inbox folder.Don't see the Quick Access Toolbar on screen? Right-click the ribbon → Show Quick Access Toolbar first (newer Outlook hides it by default).
.ps1 — or just use Way 1, no macros needed.)_Inbox and filed on the next run._Inbox.Same engine either way — the drop folder just handles the messages automation can't see because they never reach the study folder. Ops content only — never drop a participant-level / unblinded / sponsor-restricted email in here.
<SyncedLibrary>\
Notes\ <- one .html note per email (this is the KB content)
index.html <- the searchable wiki (rebuilt every run)
_Inbox\ <- ad-hoc drop folder (the exception path)
_Inbox\_processed\ <- ad-hoc originals moved here after filing
.state\seen.json <- which messages are already filed (EntryIDs)
End to end on a made-up but realistic study-operations email for the placeholder study CP-101. Everything here is illustrative — no real study, no PHI.
A bioanalytical CRO sends a logistics update; your one-time rule files it into the CP-101 Outlook folder. It's operational — timelines, a data transfer, the database-lock target — exactly what the KB is for.
From: Jordan Lee <jordan.lee@cro-bioanalytics.example> (Bioanalytical Project Lead)
Sent: Fri 2026-06-19 16:42
To: CP-101 Study Team
Subject: CP-101 — PK assay re-validation complete; transfer Mon, DBL on track
Hi team — re-validation of the CP-101 plasma PK assay is complete
(incurred-sample reanalysis passed, %CV within spec). We'll push the final
concentration transfer to the EDC on Monday 22-Jun. No change to the
database-lock target of Thu 25-Jun. Ping me if you want the validation memo.
— Jordan
You do nothing. Within ~15 minutes the job reads the folder and files every new message (here, this one plus two others that arrived since the last run):
PS> .\Update-StudyKB.ps1 -StudyFolders CP-101 # (what the scheduler runs)
[StudyKB] reading Outlook folder "CP-101"
[StudyKB] + Notes\20260619_164200__CP-101_PK_assay_re-validation_complete_transfer__7f3a9e21.html
[StudyKB] + Notes\20260620_091500__CP-101_monitoring_visit_report_filed_2_action__b042c8d5.html
[StudyKB] + Notes\20260618_173000__CP-101_EDC_extract_delivered_for_the_19-Jun__3e9170aa.html
[StudyKB] 3 new from Outlook, 0 from drop-folder; 47 total. Wiki: ...\index.html · mail left untouched in Outlook
Illustrative — generated from the code path, not a recorded run. The real note name is yyyyMMdd_HHmmss__subject-slug__ + an 8-hex MD5 fragment of the Outlook EntryID. First adoption step is a supervised dry-run on one machine against a test folder.
One self-contained HTML note lands in Notes\ — the email's metadata on top, the body below, ready to read or search:
| Subject | CP-101 — PK assay re-validation complete; transfer Mon, DBL on track |
|---|---|
| From | Jordan Lee (the sender's display name, captured verbatim) |
| Captured | 2026-06-19 16:42 |
| Body | Re-validation of the CP-101 plasma PK assay complete (incurred-sample reanalysis passed, %CV within spec). Final concentration transfer to EDC Mon 22-Jun. Database-lock target unchanged: Thu 25-Jun. |
Every run rebuilds index.html. Open it and search (Ctrl-F or the built-in filter) for DBL, assay, or CP-101 — alongside everything else the study has accumulated:
| Date | From | Subject |
|---|---|---|
| 2026-06-19 | Jordan Lee · CRO Bioanalytics | CP-101 — PK assay re-validation complete; transfer Mon, DBL on track |
| 2026-06-17 | Pat Morgan · Data Management | CP-101 — query backlog down to 12 open; SDV 96% |
| 2026-06-13 | Sam Rivera · Clinical Ops | CP-101 — last-participant-last-visit confirmed 12-Jun |
Point the on-prem model from tlf_interpret at Notes\ and you can ask in plain language — grounded only on the notes, on-device:
Q: When is the CP-101 database lock, and is it still on track?
A: Thu 25-Jun, on track. The PK assay re-validation finished 19-Jun
(incurred-sample reanalysis passed); the final concentration transfer
is Mon 22-Jun. [source: 2026-06-19 note · Jordan Lee]
The model is constrained to quote and cite a note, and is used only for operational recall — any reported or regulated value always comes from the validated system, never from a note or the model, even when the model surfaces one.
Not part of the core skeleton — the deliverable above is a deterministic file-mover with no AI. This is a later add-on; skip it in a first demo. That said: Notes\ is a small, clean corpus — good for an on-device assistant so you can ask questions over the whole study without any cloud. The flow: strip the HTML tags → embed → ask, all locally. A minimal recipe:
New-Item -Type Directory -Force "$Root\.rag" | Out-Null
Get-ChildItem "$Root\Notes\*.html" | ForEach-Object {
$t = (Get-Content -Raw $_) -replace '<[^>]+>',' ' -replace ' ',' ' -replace '\s+',' '
Set-Content "$Root\.rag\$($_.BaseName).txt" $t.Trim() -Encoding UTF8
}.rag\ — the same on-prem GGUF used in tlf_interpret (llama.cpp / its server, with a small local embedding model). Retrieve the top notes for the question, pass them as context, and require the answer to quote and cite the note it used.| Source | Fidelity |
|---|---|
| Auto-ingest (Outlook folder / Inbox filter) | High — reads each message's HTMLBody straight from Outlook, in your own session. Read-only: nothing is moved, deleted, or flagged. |
.msg (ad-hoc classic-Outlook drag) | High — read via the installed Outlook as a file reader only. |
.txt / .html (you save/paste) | Exact. |
.eml (ad-hoc new-Outlook drag) | Full fidelity if you drop MimeKit.dll next to the script (auto-detected: MimeMessage.Load → Subject/From/Date/HtmlBody). Without it, a built-in naive MIME reader handles plain/simple-HTML emails (multipart/encoded may show raw sections). |
.eml path — auto-ingest doesn't use it.)Preferred (production posture): have IT code-sign the script — or allowlist its path — which also satisfies change control. -ExecutionPolicy Bypass is the unsigned quick-start; note that ExecutionPolicy is an execution-convenience setting, not a security boundary (per Microsoft), so the signature/allowlist is the real control. If a run is blocked: Unblock-File .\Update-StudyKB.ps1 first; if still blocked, ask IT for the signature/allowlist. If your org blocks programmatic access to Outlook (the prompt never clears), the clean fix is to code-sign the script and trust that publisher in Trust Center ▸ Programmatic Access — keeping the object-model guard ON for everything unsigned — or fall back to the drop-folder path. No admin rights are otherwise needed.
| What you see | Why | Fix |
|---|---|---|
Nothing gets filed; index.html stays empty | Outlook isn't open, or your rule isn't actually moving mail into CP-101 | Open classic Outlook and sign in; check study mail is really landing in the CP-101 folder. Drop a test email into CP-101 and run the script. |
…running scripts is disabled on this system | PowerShell execution policy | Unblock-File .\Update-StudyKB.ps1 first; if still blocked, have IT allowlist or sign it (see IT policy). |
| "A program is trying to access Outlook" | Outlook's programmatic-access guard (first run) | Click Allow — the script only reads. For good, have IT sign it and trust the publisher. |
Outlook folder not found: 'CP-101' | -StudyFolders name ≠ your rule's folder | Make them identical. Nested/shared mailbox? Pass the path, e.g. -StudyFolders "Project Mailbox\Inbox\CP-101". |
Auto-ingest skipped: classic Outlook isn't running | Outlook is closed (or it's new Outlook / Mac) | Open classic Outlook. New Outlook / Mac: use the drag-to-_Inbox path instead. |
| The wiki isn't where I expected | The KB root is the script's own folder | Read the [StudyKB] root = … line it prints — that's exactly where it's writing. Put the script in your synced library. |
My dropped email vanished from _Inbox | It was filed | That's success — the original moved to _Inbox\_processed (kept, not deleted). |
| Older emails didn't get picked up | The first run only back-fills 30 days | Widen it once: add -BackfillDays 90 (or more) to the run command. |
| The scheduled task never fires | Created as SYSTEM/stored-password, or you're logged off | Recreate it with /IT (interactive) and stay logged on with Outlook open — see step ③. |
Still stuck? Run the script by hand (the test in step ⑩) and read the [StudyKB] / WARNING lines — they say exactly what it did and didn't do.
Operations content only — no PHI, no participant-level data, no unblinded/sponsor-restricted material in the knowledge base. The auto-ingest files whatever reaches the study folder, and an Outlook rule can only filter by source and keywords — not by understanding content. So keep participant data out with a layered control, not one clever rule: (1) you review what's filed, and the KB is ops-only by policy; (2) route the folder from your operational senders/distros and except the sensitive channels (safety/PV, EDC/IVRS/randomization, unblinded-stats, lab portals); (3) it assumes a blinded operational mailbox — sensitive channels can reach a blinded inbox, so the except-list is a precondition, not a nicety; if the operator is unblinded by role, don't auto-ingest their Inbox. Reported or regulated numbers always come from validated tools, never from notes or an LLM summary of them.
Not budget, not a new system, not production use — just approval to run a small, governed pilot:
It's the lowest-risk piece of the program — governed, reversible, and self-contained — which makes it the right place to prove the operating pattern (pilot → gate → decide) that the rest of the program follows. The wins it's after are shared team memory (the study's history survives someone being on leave), consistency (one searchable record instead of scattered inboxes), and speed of recall — with sensitive data kept in-tenant.
Update-StudyKB.ps1PowerShell — the scheduled engine: reads your Outlook study folder(s)/Inbox-filter, files every new message as an HTML note (read-only, EntryID-tracked), also drains the ad-hoc _Inbox, and rebuilds index.html
FileToKB.vbaClassic-Outlook macro — toolbar one-click for the ad-hoc path: saves selected email(s) into _Inbox as .msg