Skip to content

About

Outlook Smart Alerts add-in for recipient, attachment, subject, and body send confirmation

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

138 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English | 日本語

Mail Lookout

A send-confirmation add-in for new Outlook and Outlook on the web.

It runs when you press Send. Outlook's built-in Smart Alerts dialog opens a review task pane with checkboxes for recipients, attachments, subject, and body. After everything is checked, the review pane sends the message. The goal is to stop the small mistakes: the wrong recipient, the forgotten attachment, the empty subject.

Scheduled send / Send later is not supported. When Outlook has a future delivery time on the draft, Mail Lookout skips its review flow and lets Outlook schedule the message unchanged. Scheduled messages are therefore not checked by this add-in.

The name is literal: a lookout for your outgoing mail — a quiet watch that flags problems before a message leaves.

Japanese version: README.ja.md

Install

Mail Lookout is publicly available on Microsoft Marketplace: get Mail Lookout for Outlook. Open the listing, select Get it now, and follow the Microsoft 365 installation flow. No manifest download or sideloading is required for normal installation.

Features

This add-in does four things at send time.

  1. Recipient check. It lists every recipient by field (To, Cc, Bcc) and marks external ones.
  2. Attachment check. It lists every real attachment.
  3. Body check. It shows a preview of the body and asks you to review it.
  4. Review pane confirmation. It blocks the first send attempt and opens a task pane where required items must be checked.

It also raises two warnings:

  • Empty subject. Requires an explicit check when the subject is blank.
  • Forgotten attachment. A warning when the body mentions an attachment ("see attached", "添付") but no file is attached.

A Settings task pane lets each user adjust the internal domains and the default send-delay. Those are stored per user in Outlook roaming settings, so they follow the user across devices. Everything else — and the shipped defaults — lives in one config file, src/config/defaults.ts.

Screenshots

On Send, Outlook's Smart Alerts opens the review pane; once the checklist is confirmed, a cancellable countdown sends the message.

The review pane in Outlook on the web:

Mail Lookout review pane in Outlook on the web

Up close — the review checklist and the send-wait countdown:

Review pane: recipients with external badge, attachments, subject, body Send-delay countdown with cancel and back

The built-in Smart Alerts message that opens the review:

Smart Alerts message on Send

Requirements

  • New Outlook on Windows, or Outlook on the web.
  • Mailbox requirement set 1.15 or later.
  • Bun 1.3 or later for development.

This add-in does not run on Outlook mobile. See the limitations section for classic Outlook on Windows.

Architecture

The code is split so the logic does not depend on Office.

src/
  domain/    Pure logic. No Office, no DOM, no time. Fully tested.
  config/    The config shape and its defaults.
  i18n/      Type-safe messages. One file per language.
  shared/    Shared message shapes used by the browser-only preview.
  office/    The Office adapter. Reads the draft, runs the Smart Alerts handler.
  commands/  Registers the send handler with Office.
  dialog/    Renders the browser-only confirmation preview.

The domain layer is the core. It takes a plain snapshot of the message plus the config, and it returns a flat, JSON-serializable model. It decides what to show and what to require. Because it touches nothing from the host, every rule is tested with plain data. The tests are where the value is.

The office layer is a thin adapter. It reads the draft through the Office APIs, hands a plain snapshot to domain, and then uses Outlook's built-in Smart Alerts dialog to cancel or allow the send. The core layers import nothing from Office, so the boundary holds by construction; keep any new host calls in the office layer.

Development setup

# 1. Install dependencies.
bun install

# 2. Trust a local HTTPS certificate. Outlook requires HTTPS.
bun run dev-certs

# 3. Start the dev server on https://localhost:3000.
bun run dev:outlook

Then sideload manifest.xml in Outlook for local development. The steps depend on the host:

  • Outlook on the web: open Settings, go to the add-ins page, choose "Add a custom add-in" then "Add from file", and pick manifest.xml.
  • New Outlook on Windows: use the same add-ins management page, reached from Outlook on the web with the same account.

After sideloading, open a new message, fill in a recipient, and press Send. Outlook's Smart Alerts dialog should appear. Choose "Open review", check the required items in the task pane, then press Send again without changing the draft.

Local emulator without Outlook

You can test the review flow in a browser without Outlook:

bun install
bun run dev:emulator

Or start the normal dev server and open /emulator.html on the URL Vite prints. If port 3000 is busy, Vite will choose the next free port, for example https://localhost:3001/emulator.html. The emulator uses the same domain logic and dialog renderer as the Outlook send handler, but it does not load Office.js or call Outlook APIs. Edit the draft fields, switch scenarios, then click "Review send" to rebuild the dialog.

For Outlook sideloading, use bun run dev:outlook. The manifest is fixed to https://localhost:3000, so that script intentionally fails if port 3000 is already in use.

Verify the code

bun run check

This runs, in order: lint (oxlint) and format check (oxfmt), type check on both tsconfigs, tests with coverage, the production build, and manifest validation. Each step must pass. Useful single steps:

bun run typecheck      # tsc on src and on the build config
bun run lint           # oxlint
bun run format         # oxfmt --write
bun run test           # vitest, run once
bun run test:coverage  # vitest with coverage on the pure layers
bun run build          # tsc --noEmit then vite build
bun run validate       # office-addin-manifest validate

Production deployment

The Marketplace release loads the hosted add-in from Cloudflare Pages. The repository includes wrangler.toml, so Pages can build with bun run build and publish dist/. During that build, scripts/generate-manifest.js writes dist/manifest.xml with https://avishaikofun.com embedded.

The add-in runtime is served from the apex, avishaikofun.com, and that host belongs to this repository. The company site is not here: it lives in hjosugi/avishaikofun-site and is served from www, so a copy edit to a marketing page no longer goes through this project's build, tests, and release gates. The apex root redirects there (public/_redirects); every other path is the add-in.

The Mail Lookout product pages — support.html, privacy.html, terms.html — stayed behind on purpose. The manifest hard-codes SupportUrl and the Marketplace listing points at those URLs, so moving them would mean re-certification.

End users should install from the Microsoft Marketplace listing; the hosted /manifest.xml remains available for development and testing.

Tagged releases also attach mail-lookout-manifest.xml on the GitHub Releases page. Use that file when you want a fixed version instead of the latest hosted manifest.

To bump the patch version, commit it, push the branch, and push the release tag for GitHub Releases:

bun run version:patch

Use bun run version:minor, bun run version:major, or bun run version:set 1.2.3 for other version changes. These commands update package.json and manifest.xml, create the version commit, push the current branch, then create and push the v* release tag. GitHub Actions creates the release asset from that tag. The script shows the next version first and asks for a y/N confirmation before changing files. Use bun run version:bump patch when you only want the local version commit without pushing or tagging.

See CLOUDFLARE.md for the step-by-step flow.

The source manifest still ships with placeholder values. Replace them before a production or marketplace release.

  1. GUID. Replace the <Id> in manifest.xml with your own GUID.
  2. URLs. Cloudflare Pages is configured for https://avishaikofun.com. For another host, replace every https://localhost:3000 in manifest.xml with your host or run ADDIN_HOST_URL=https://your-domain.example bun run build. Serve the dist/ folder over HTTPS at that host. The entry JS keeps a stable name (/assets/commands.js), so the manifest URLs do not change between builds.
  3. Internal domains. Edit internalDomains in src/config/defaults.ts. The shipped default is avishaikofun.com. If this list is wrong, every recipient looks external.
  4. Metadata. Replace ProviderName, SupportUrl, and AppDomains in manifest.xml.

Then publish through the Microsoft 365 admin center for your organization, or sideload for a single user.

Mail Lookout is publicly available from Microsoft Marketplace. For future Marketplace updates, deploy and verify the hosted build, bump the manifest version, and submit the updated package through Partner Center.

For the Marketplace description and certification notes, see docs/marketplace-resubmission.md.

Configuration

The shipped defaults live in src/config/defaults.ts — fork that file to change them. At runtime, the Settings task pane overrides a subset per user, marked below. The main options:

  • internalDomains: domains treated as internal (also editable in the Settings pane).
  • sendDelaySeconds: the default countdown before a confirmed message is sent (also editable in the Settings pane).
  • requireRecipientConfirmation: include recipients in the send-time confirmation (also editable in the Settings pane).
  • requireAttachmentConfirmation: include attachments in the send-time confirmation (also editable in the Settings pane).
  • requireBodyConfirmation: include the body preview in the send-time confirmation (also editable in the Settings pane).
  • allowSendAnyway: offer Outlook's Send Anyway on a blocked send (also editable in the Settings pane). Off by default; see SendMode for what it can and cannot reach.
  • attachmentKeywords: words that hint the body refers to an attachment, used by the forgotten-attachment warning.
  • warnOnEmptySubject: warn when the subject is blank.
  • fallbackLocale: language used when the host language is unknown.
  • dialog: dialog width and height as a percent of the screen.

Add a language

The messages are type-safe. To add a language:

  1. Copy src/i18n/locales/en.ts to a new file, for example de.ts, and translate every value.
  2. Add one line to src/i18n/catalog.ts: import it and add it to the locales object.

The compiler will tell you if you miss a key. A test in test/i18n.test.ts also checks that every locale has the same set of keys. Nothing else needs to change. The locale tag type updates itself from the keys of locales.

SendMode

The manifest uses SendMode="SoftBlock". When the add-in cancels a send, the user must go back and edit the draft. By default there is no one-click "send anyway" path. This is on purpose: a confirmation tool whose every cancel is one click to bypass does not confirm much.

The first send attempt shows the Smart Alerts dialog and cancels the send. The dialog's action button opens a task pane with the checkbox review UI. After the task pane marks the draft as reviewed, it sends the message through Outlook's compose API. If any unexpected error happens, the handler cancels the send. It never sends real mail without confirmation.

What SoftBlock does not cover

SendMode also decides the case the add-in cannot decide itself: what happens when the runtime never loads, because the host is unreachable or a browser extension blocked the frame. Under SoftBlock Outlook sends the message — no dialog, no warning, nothing in a log. The add-in is silently absent rather than visibly broken.

Block is the only mode that refuses the send there, and it is not available to us. AppSource rejects the manifest:

Error #1: Block SendMode is not allowed

PromptUser does not help either — it also sends when the add-in is unavailable, and it would additionally put a permanent bypass button on every cancel. So for a Marketplace-distributed add-in this gap cannot be closed, only monitored: scripts/heartbeat.js checks the host every six hours. Closing it requires distributing the manifest yourself, by sideloading or admin deployment, with SendMode="Block".

Relaxing it per user

Settings → When a send is blocked → Offer Send Anyway adds a Send Anyway button to a canceled send. It is off by default.

The direction is forced by the API, not chosen. sendModeOverride accepts exactly one value, promptUser, so a running handler can loosen the mode the manifest declares but can never tighten it. Strict therefore has to be what ships in the manifest, with this as the opt-out — shipping PromptUser and letting a setting harden it is not expressible.

The same asymmetry sets the limit, and the Settings pane says so next to the checkbox: the setting only applies when the add-in actually ran. It cannot change the failed-to-load case above, in either direction.

Limitations

Be honest about what this is and is not.

  • Custom Office dialogs can't be opened from the send event. OnMessageSend runs through event-based activation, and Office UI APIs such as Office.context.ui.displayDialogAsync are blocked there. The production send flow therefore uses the built-in Smart Alerts dialog instead of the browser preview dialog.
  • The send delay runs in the review pane. Outlook's Smart Alerts handler must stay short-running, so the task pane owns the countdown and then calls Outlook's compose send API. Closing or refreshing the pane cancels that pending send.
  • Scheduled send / Send later is intentionally bypassed. Mail Lookout checks delayDeliveryTime at send time. If a future delivery time is set, the add-in allows the event immediately and doesn't open the review pane. This avoids converting a scheduled message into an immediate sendAsync send, but it also means Mail Lookout does not protect scheduled messages.
  • Classic Outlook on Windows is not a target. That host uses a JavaScript-only runtime for send handlers. This project builds an ES module that loads through an HTML page in a browser runtime, which is what new Outlook and Outlook on the web use. The manifest declares a JS-only override for schema reasons, but the classic path is not supported or tested.
  • Outlook mobile is not supported. Smart Alerts on send do not run there.
  • If the add-in cannot load, the message sends unchecked. Outlook loads commands.html from the host on every send. If that fails — host down, extension blocking the frame — SoftBlock lets the send through with no dialog and no warning, and no setting can change it, because none of our code runs. Only SendMode="Block" refuses there, and AppSource does not allow Block for a Marketplace add-in. This is the add-in's real failure mode; scripts/heartbeat.js watches the host every six hours to keep it rare. See SendMode.

Disclaimer

Use this add-in at your own risk. The authors and contributors are not responsible for any damages, losses, misdelivery, business interruption, or other liability arising from the use of, inability to use, deployment of, or modification of this software. Review the configuration and behavior before using it in a production environment.

Relation to OutlookOkan

OutlookOkan is an existing send-confirmation tool for Outlook. Mail Lookout respects the work and the problem it addresses.

Mail Lookout is an independent project. It is not affiliated with, endorsed by, or sponsored by OutlookOkan or its author.

Migrating to the unified manifest

This project ships an add-in only manifest (manifest.xml), which is well supported on Outlook on the web and new Outlook today. The unified manifest for Microsoft 365 is the newer format and is the direction Microsoft is moving. If you need it, the office-addin-manifest tool can convert an XML manifest to the unified format. The runtime code in this project does not change; only the manifest does.

License

0BSD. You can use, copy, modify, and distribute this project for almost any purpose.

MIT

About

Outlook Smart Alerts add-in for recipient, attachment, subject, and body send confirmation

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages