# Capturing Email and Calendar Without a Mailbox API

**Author:** Ivan Misic  
**Published:** 2026-07-22  
**URL:** https://ivanmisic.net/blog/ai-tools/email-calendar-capture-without-api

> Microsoft Graph and IMAP were off the table, so I used Power Automate, OneDrive sync, and one 15-minute scheduled task to pull email and calendar into a local inbox.

Your email and calendar are the richest record of your work week, and usually the most locked down. In [the overview of my Obsidian and Claude setup](/blog/ai-tools/claude-obsidian-second-brain) I mentioned getting both into a local inbox with no API access at all. This is how, in enough detail to copy.

## The obvious routes are closed

The clean way to read a corporate mailbox from a script is an API: Microsoft Graph, or plain old IMAP. In a lot of workplaces neither is realistic for a personal project. Graph wants an app registration and admin consent. IMAP is often switched off. The front door is locked, and it's locked for good reasons.

The side door is a tool your company is probably already paying for. Power Automate is Microsoft's own workflow automation, it has blessed connectors to your mailbox and calendar, and it can write files to your OneDrive. OneDrive syncs those files to your laptop. From there, a tiny scheduled task drops them into the vault. Three dumb pieces, none of which asks for any permission you don't already have.

## The shape of it

The whole path runs one direction, mailbox to local folder:

![The capture path as five stages left to right: mailbox, Power Automate, OneDrive, a timer, and the vault folder, with a branch off OneDrive into a Processed folder](/images/blog/ai-tools/email-capture-path.png)

Every hop is dumb and independent. If OneDrive is slow, the flow still runs. If the laptop is off, the files wait in the cloud. Nothing holds a connection open, so nothing has a connection to lose. That property is the whole reason this beats a "proper" integration, and I'll come back to it.

## Flow 1: capture the mail you care about

Received mail is captured by flag, not by firehose. I don't want every email in the vault, I want the ones that matter, so the flow triggers on **When an email is flagged (V3)** in the Inbox. I flag a message in Outlook, the flow fires, fetches the full email, converts the HTML body to plain text, and writes a `.txt` into `EmailCapture/Vault`. Then it unflags the message so it can't fire twice. Any attachments go alongside it in `EmailCapture/Vault/Attachments`, numbered so two files called `report.pdf` can't collide. If I tag the email with an Outlook category first (VIP, Follow Up, Waiting), the flow captures that too, and falls back to `Uncategorized` when I haven't.

The file the Compose step assembles comes out like this:

```
Type Category: VIP
Type From:  sender@example.com
Type To:  you@example.com
Type CC:
Type Subject:  RE: Q2 Budget Review
Type Date:  2026-03-06T14:30:00Z
Type ConversationId:  AAQkAGI2...

Could you review the attached budget before Thursday...
```

That `ConversationId` is Outlook's thread identifier, captured so the vault can group a reply chain with certainty instead of guessing from the subject line. The `Type ` prefix on every header is a Power Automate quirk, and I'll come back to it.

## Flow 2: capture everything you send

Sent mail is the opposite. I want all of it with no effort, so that flow triggers automatically on every new item in **Sent Items**. A condition filters out the automatic meeting accept and decline replies so they don't become noise, then it converts the body to text and writes a `.txt` into `EmailCapture/Sent`. No flag, no thought.

That condition matches on the subject prefix, so it needs every language your calendar replies arrive in. Mine checks eleven prefixes across English, German, and Croatian, because a `Declined:` filter does nothing about an `Abgelehnt:` or an `Odbijeno:`. If your work spans more than a couple of countries, that list only grows, and every language you miss is a category of noise you'll be deleting by hand later. Check your own sent folder before you assume one covers it.

The sent file uses plain headers, and the `From` field comes out empty:

```
From:
To: colleague@example.com
CC:
Subject: RE: Some Subject Here
Date: 2026-03-06T14:30:00Z
ConversationId: AAQkAGI2...

Email body in plain text...
```

## The two-formats gotcha

Look at the two examples. Received mail prefixes every header with `Type `, sent mail doesn't, and sent mail has an empty `From`. That isn't a choice, it's how the two triggers serialize their data. Instead of fighting Power Automate into agreement, I let the parser downstream read both shapes. The capture layer has exactly one job: get each file into the right folder. That empty `From` on sent mail is the reason the bridge script matters later.

## Flow 3: the senders you can't afford to miss

Flagging works on the days I'm reading my inbox. It fails on the days I'm not, and those are exactly the days something from the wrong person sits unread until Thursday. So the third flow watches people instead of messages.

The tagging half isn't Power Automate at all. An ordinary Outlook rule puts a `VIP` category on anything from the senders I list, the moment it arrives. Then the flow wakes up every 15 minutes and asks the mailbox for `category:VIP NOT category:VIP-Captured`, takes up to ten at a time, writes each one out the way Flow 1 does, and stamps the message with a second category, `VIP-Captured`.

That second category is the trick. Flow 1 can unflag a message to stop it firing twice, but this one can't clear the `VIP` tag, because I still want it there when I go looking. So it adds a marker instead, and the search query filters on it. There's no list of processed IDs anywhere, and if I ever want a message recaptured I delete one category by hand.

One difference from Flow 1 that matters downstream: VIP mail lands in its own `EmailCapture/VIP/Vault` folder, so the bridge script treats it as a separate source. Otherwise it behaves the same, attachments included.

## And the calendar

The calendar is one more flow, scheduled to run every 30 minutes through the workday. It uses **Get calendar view of events (V3)** for today's range, a **Select** to keep the fields I care about (subject, start, end, organizer, attendees, online meeting link), and a **Create file** that writes `yyyy-MM-dd-calendar.json` into `EmailCapture/Calendar`. Because it overwrites the same day's file on each run, the calendar is always one fresh snapshot instead of a pile of duplicates.

## The bridge: one scheduled task

The files are now in OneDrive, which syncs to the laptop. The last hop is a small PowerShell script on a 15-minute timer. It walks each capture folder in turn: received mail into the inbox as-is, VIP mail the same way from its own folder, sent mail with a `SENT-` prefix, and the calendar snapshot refreshed. The core of it, trimmed to three of the four:

```powershell
$Sent     = "$env:USERPROFILE\OneDrive\EmailCapture\Sent"
$Received = "$env:USERPROFILE\OneDrive\EmailCapture\Vault"
$Calendar = "$env:USERPROFILE\OneDrive\EmailCapture\Calendar"
$Inbox    = "$env:USERPROFILE\Obsidian\WorkVault\00-Inbox"

# Sent mail -> inbox, tagged so the direction survives
Get-ChildItem $Sent -Filter *.txt | ForEach-Object {
    Copy-Item $_.FullName (Join-Path $Inbox "SENT-$($_.Name)")
    Move-Item $_.FullName (Join-Path "$Sent\Processed" $_.Name)
}

# Received mail -> inbox as-is
Get-ChildItem $Received -Filter *.txt | ForEach-Object {
    Copy-Item $_.FullName (Join-Path $Inbox $_.Name)
    Move-Item $_.FullName (Join-Path "$Received\Processed" $_.Name)
}

# Calendar -> overwrite today's snapshot
Get-ChildItem $Calendar -Filter *-calendar.json | ForEach-Object {
    Copy-Item $_.FullName (Join-Path $Inbox $_.Name) -Force
}
```

The full version adds a `-2` suffix when a filename already exists and rotates its own log. But that is the whole idea: copy, mark as done, move on.

The `Move-Item` into `Processed` is doing more work than it looks like. Once a file has been copied into the vault it leaves the capture folder entirely, so the next run has nothing to reconsider, the same email can't arrive twice, and the OneDrive folder doesn't quietly grow forever. Every file gets exactly one trip, and the originals are still sitting in `Processed` if you ever need to prove what came in.

That `SENT-` prefix is the dumbest and most useful line in the file. Power Automate hands me sent mail with an empty `From`, so the content alone can't tell me which way a message went. Four characters added at copy time settle it for good, and every step downstream just reads the filename.

## Put it on a timer

Register the script as a scheduled task that fires every 15 minutes while you're logged in:

```powershell
$Action  = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\path\to\Pull-Emails.ps1"
$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date `
    -RepetitionInterval (New-TimeSpan -Minutes 15)
Register-ScheduledTask -TaskName "PullEmails" -Action $Action -Trigger $Trigger `
    -Description "Copies captured mail and calendar into the vault inbox"
```

If the console window flashing every fifteen minutes bothers you, and it will, wrap the call in a one-line `.vbs` launched with `wscript.exe` so it runs fully hidden. Tiny thing, real quality-of-life gain.

### If your vault already lives in OneDrive

Then you can skip most of this. Point the Power Automate **Create file** step straight at the vault's inbox folder and the files arrive on their own, no scheduled task anywhere.

I kept the task, and the reason is the three small things it does on the way past: the `SENT-` prefix, the `Processed` move, and the duplicate-name suffix. Write directly into the inbox and you lose all three, which means sent and received mail become indistinguishable and nothing ever gets swept out of the capture folder. If you don't need that distinction, take the shorter path. I did need it, so I pay one scheduled task for it.

## What the AI actually sees

Worth being exact here, because "my work email goes into a folder an AI reads" deserves a straight answer.

Everything described so far moves your real mail. The file that lands in your inbox has real names and real addresses in it, and nothing is masked on this leg. What it has going for it: nothing leaves your company's tenant, your OneDrive, your disk. The mail was already sitting in all three.

The masking happens one step later, before anything reaches a model. A script swaps addresses, phone numbers, and the other patterns that scan as private for tokens, and keeps the real values in a local map the model never gets handed. I went through that in [the overview of the setup](/blog/ai-tools/claude-obsidian-second-brain). It lowers what gets sent rather than pretending to eliminate it, and it's honest about the gap: subject lines and meeting titles still go out roughly as written.

So the capture layer is not the privacy layer, and this post only covers the first one.

## Why text files instead of a real integration

This looks primitive next to a proper API client, and that is the point. A token-based integration is a thing that breaks. Tokens expire, scopes change, rate limits bite, a refresh quietly fails overnight and the sync is down until you happen to notice. A folder of text files has none of those failure modes. Each email is one file. Either it copied or it didn't, and if it didn't, the next run grabs it. Keeping the transport alive is OneDrive's job, and OneDrive is very good at its job.

## What this won't do

A few honest limits:

- It needs Power Automate, which usually means a Microsoft 365 business plan, and some licenses cap how many flow runs you get per day.
- It isn't real-time. Worst case, a message waits about 15 minutes before it reaches the inbox. Fine for a once-a-day review, not fine for a live assistant.
- The scheduled task is Windows. The same pattern runs on a Mac or Linux box with `cron` and the OneDrive client; the script is just shell instead of PowerShell.
- Check your own policy. Exporting mail to files, even your own mail, may or may not be allowed where you work. This is a know-the-rules situation, not an ask-forgiveness one.

## The boring part is the important part

None of this is clever. It's a flow, a sync folder, and a timer, held together by a four-character filename prefix. But it's the piece everything else leans on, because the smartest pipeline in the world does nothing until the data actually shows up. Get the dull part right and the interesting parts get to exist.

I don't think this is the permanent shape of it. My guess is that within a few years the assistant just has a sanctioned connector to your mailbox, IT is comfortable switching it on, and none of this plumbing is anyone's problem. That's a prediction, not a roadmap, and I've been wrong about timelines before. What I'm confident about is the gap in between: right now there's a real distance between what these tools could do with your work context and what your IT department will let them touch. A flow and a sync folder is what half-automation looks like while you wait. I'd rather run the boring version today than wait for the clean one to get approved.

Next: what happens to all of this once it lands, and why [most of that pipeline isn't AI at all](/blog/ai-tools/script-first-ai-pipeline).
