Nabheet Madan3 downloadsSync Microsoft 365 into your vault: Outlook mail (one note per email + thread index), calendar events, and Teams messages (chats + channels). Incremental sync, PKCE auth, read-only scopes.
Sync your Microsoft 365 into your Obsidian vault — mail, calendar, and Teams messages — from a single Azure app. Incremental sync, PKCE auth (no client secret stored), read-only scopes, configurable folder mappings.
📖 Setting up? See SETUP.md for the full step-by-step guide: Azure app registration, a per-feature permissions matrix, admin consent, and troubleshooting.
Desktop-only (needs Node
http/cryptofor the OAuth loopback). Not for Obsidian mobile.
Three features, each independently toggleable, all under one target folder
(default 10-Mailbox):
<target>/<subfolder>/<date> <subject> <id>.md
with YAML frontmatter (from/to/cc, dates, conversation id, web link, flags)._Thread Index.md per subfolder, grouping messages by conversation.📅 Calendar (10-Mailbox/Calendar/)
💬 Teams (10-Mailbox/Teams/)
_Conversation Index.md.📖 New here? See SETUP.md for the full step-by-step guide with a per-feature permissions matrix (Mail / Calendar / Teams), admin-consent notes, and an error-code troubleshooting table. The summary below is the quick version.
You need a free Azure AD app registration (public client). No secret is created or stored — auth uses Authorization Code + PKCE.
Obsidian Outlook Teams and Calendar (anything).http://localhost
(Azure allows http://localhost on any port for this platform — the plugin
picks a free port at login time.)Mail.ReadUser.Readoffline_accessCalendars.Read (for calendar sync)Chat.ReadTeam.ReadBasic.All, Channel.ReadBasic.All,
ChannelMessage.Read.All (these three typically need admin consent)
The plugin only requests the Teams scopes when you enable those features, so
add them only if you'll use Teams sync.AADSTS65001/AADSTS90094.Then in the plugin settings:
organizations for any
work/school account, or common for any account type.From this repo:
npm install
npm run build # produces main.js
Copy main.js, manifest.json, and styles.css into your vault:
<your-vault>/.obsidian/plugins/outlook-teams-calendar/
Or use the helper (set your vault path):
VAULT="/path/to/your/vault" npm run install:vault # see scripts/install.mjs
Then in Obsidian: Settings → Community plugins → Installed plugins → enable Outlook Teams and Calendar. Reload plugins if it doesn't appear.
Open Settings → Outlook Teams and Calendar:
10-Mailbox).| Outlook path | Vault subfolder |
|---|---|
inbox |
Inbox |
archive |
Archive |
Projects/ClientX |
Clients/ClientX |
inbox, archive, sentitems,
deleteditems, junkemail, drafts, …) resolve directly./-separated for nesting.0 disables). Toggle Sync on
startup.Sync manually anytime via the mail ribbon icon or the "Outlook Teams and Calendar: Sync now" command.
10-Mailbox/
Inbox/
_Thread Index.md
2026-07-04 Quarterly numbers a1b2c3d4e5.md
2026-07-03 Re Quarterly numbers f6g7h8i9j0.md
Archive/
_Thread Index.md
...
Each email note:
---
source: outlook
message_id: AAMk...
conversation_id: AAQk...
subject: Quarterly numbers
from: [email protected]
from_name: Alice Smith
to:
- [email protected]
received: 2026-07-04T09:12:00Z
folder: Inbox
has_attachments: false
web_link: https://outlook.office365.com/...
---
# Quarterly numbers
**From:** Alice Smith <[email protected]>
**To:** Nabheet Madan <[email protected]>
**Date:** 2026-07-04T09:12:00Z
**[Open in Outlook](https://outlook.office365.com/...)**
---
<email body as Markdown>
@odata.deltaLink.data.json.Enable Settings → Calendar → Sync calendar.
10-Mailbox/Calendar/, plus an
Upcoming meetings sidebar pane (ribbon icon / command to open).calendarView (expands recurring events into instances);
it re-reads the window each sync rather than a delta cursor.Enable Settings → Teams → Sync Teams, then pick chats and/or channels. After toggling, click Reconnect so the new Graph scopes are consented (channel scopes usually need an admin).
10-Mailbox/Teams/ by default, plus a regenerated _Conversation Index.md.
Chats are titled by topic or participants; channels as Team / Channel.data.json stays small (only a cursor + last-written
timestamp per conversation are persisted)./chats/{id}/messages/delta returns
"Change tracking is not supported against microsoft.graph.chatMessage").
Instead, chats are listed newest-first and paged only back to the sync
window; the per-conversation last-written timestamp handles dedup. Practical
effect: keep the window reasonable (see below) so chat listing stays cheap.0 = all history (heavy for chats — every sync walks the full chat).📬 Outlook · <last sync time> / syncing… / error.[obs-mail] console output
(open devtools with Ctrl/Cmd+Shift+I).data.json inside your vault,
in plain text (same as most Obsidian plugins). Anyone with read access to
your vault files can use it. Keep your vault private; don't sync data.json
to untrusted locations. Use Disconnect to revoke locally, and revoke the
app under My Account → Apps & services / Azure to invalidate server-side.User.Read + offline_access (always), Mail.Read (mail), Calendars.Read
(calendar), Chat.Read (Teams chats), and Team.ReadBasic.All +
Channel.ReadBasic.All + ChannelMessage.Read.All (Teams channels). The
plugin never writes to or deletes anything in Microsoft 365.| Symptom | Cause / fix |
|---|---|
AADSTS65001 / consent required |
Grant admin consent for the app permissions (step 8). |
AADSTS7000218 / public client |
Set Allow public client flows = Yes (Authentication blade). |
| Login browser opens but nothing happens | Loopback port blocked by firewall; retry, or check devtools console. |
Outlook folder not found |
Use the exact display name; /-separate nested folders; well-known names are lowercase (sentitems, not Sent Items). |
| Nothing new syncs | State is delta-based; use Reset sync state to force full re-enumeration. |
| Conditional Access blocks device | This plugin uses the interactive auth-code flow, not device code — sign in through the opened browser as normal. |
npm install
npm run dev # esbuild watch, rebuilds main.js on change
npm run typecheck # tsc --noEmit
npm run build # typecheck + minified production bundle
Source layout:
src/main.ts — plugin lifecycle, commands, ribbon, status bar, timer.src/auth.ts — PKCE loopback OAuth flow + token refresh.src/graph.ts — Graph client: token lifecycle, folder resolution, delta.src/notes.ts — HTML→Markdown, note + thread-index writing.src/sync.ts — per-folder delta orchestration + thread bookkeeping.src/settings.ts — settings UI.src/types.ts — shared types + defaults.Released under the MIT License © 2026 Nabheet Madan.
This project is an independent, community-built plugin. It is not affiliated with, endorsed by, or sponsored by Microsoft. "Microsoft 365", "Outlook", and "Microsoft Teams" are trademarks of Microsoft Corporation.