# Why My Outlook Add-in Lies About Its Version (And Yours Should Too)

- Date: 2026-05-12
- Author: Jeppe Spanggaard
- Description: Learn why declaring a low Mailbox requirement set in your Outlook add-in manifest is the key to mobile support, and how to feature-detect newer APIs at runtime.
- URL: https://jeppe-spanggaard.dk/blogs/outlook-addin-manifest-requirement-sets-mobile/
- Tags: Outlook, Office Add-ins, Office.js, TypeScript, Mobile


I recently built an Outlook add-in. It archives emails and attachments to SharePoint, lets you attach cloud files while composing, the works. It ran great on desktop, great in Outlook on the web. Then I opened Outlook on my phone to test it there.

The add-in was gone.

Not broken. Not throwing errors. Just... not there. No button, no task pane, nothing. Outlook mobile acted like my add-in didn't exist.

## TL;DR

Your manifest's `MinVersion` is a **filter**, not documentation. If you declare the requirement set your *best* feature needs, every host that doesn't support that version will hide your add-in completely. Declare the lowest version your *minimum viable experience* needs instead (for my add-in that's Mailbox 1.5), then use `Office.context.requirements.isSetSupported()` in your code to unlock the newer stuff where it's actually available.

## The Mistake That Feels Like the Right Thing

Here's the trap. My add-in uses `getAsFileAsync()` to grab the full email as a file before uploading it to SharePoint. That API lives in the **Mailbox 1.14** requirement set. So the obvious, honest, "do the right thing" move is to declare that in the manifest:

```xml
<Requirements>
  <Sets>
    <Set Name="Mailbox" MinVersion="1.14" />
  </Sets>
</Requirements>
```

Makes sense, right? The manifest should declare what the code needs. That's what a manifest is *for*.

Wrong.

Outlook reads that `MinVersion` and treats it as a door policy: *"You need Mailbox 1.14 to see this add-in at all."* Outlook on iOS and Android supports Mailbox 1.5 (plus a handful of newer APIs cherry-picked on top). 1.5 is less than 1.14, so mobile doesn't fail gracefully, it doesn't show a warning, it just never lets your add-in through the door. That's why my add-in vanished on my phone.

The manifest version isn't describing your code. It's deciding who gets to see your add-in.

## The Fix: Degrade the Manifest, Upgrade at Runtime

The fix felt dirty the first time I did it: I "degraded" the manifest down to Mailbox 1.5, even though my code is full of 1.13 and 1.14 APIs.

This is what my add-in actually ships with:

```xml
<Requirements>
  <Sets>
    <Set Name="Mailbox" MinVersion="1.5" />
  </Sets>
</Requirements>
```

And the same low floor inside `VersionOverrides`:

```xml
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
  <Requirements>
    <bt:Sets DefaultMinVersion="1.5">
      <bt:Set Name="Mailbox"/>
    </bt:Sets>
  </Requirements>
  ...
</VersionOverrides>
```

Why 1.5? Because that's what the *minimum viable version* of my add-in needs: open a task pane, read the current message. Everything above that is a bonus feature, and bonus features shouldn't decide whether the add-in exists.

One more thing mobile needs, and this one is easy to miss: mobile only renders what you explicitly declare in a `MobileFormFactor`. Desktop extension points don't carry over.

```xml
<MobileFormFactor>
  <ExtensionPoint xsi:type="MobileMessageReadCommandSurface">
    <Group id="mobileReadGroup">
      <Label resid="groupLabel" />
      <Control xsi:type="MobileButton" id="mobileReadOpenPaneButton">
        <Label resid="taskPaneButtonLabel" />
        <Icon xsi:type="bt:MobileIconList">
          <bt:Image size="25" scale="1" resid="icon25" />
          <!-- ...more sizes and scales... -->
        </Icon>
        <Action xsi:type="ShowTaskpane">
          <SourceLocation resid="messageReadTaskPaneUrl" />
        </Action>
      </Control>
    </Group>
  </ExtensionPoint>
</MobileFormFactor>
```

Low floor + `MobileFormFactor`, and suddenly the add-in shows up on my phone. 🎉

## Now Your Code Has to Keep the Promise

Here's the deal you just made: the manifest promises Outlook almost nothing, so your code can't *assume* anything. Every API newer than your floor needs a runtime check before you touch it.

Office.js has exactly the tool for this: `isSetSupported()`. I keep all of mine in one file, `platformDetection.ts`:

```typescript
export function isMobilePlatform(): boolean {
  if (typeof Office === 'undefined' || !Office.context?.diagnostics?.platform) {
    return false;
  }

  const hostInfo = Office.context.diagnostics.platform;
  return hostInfo === Office.PlatformType.iOS ||
         hostInfo === Office.PlatformType.Android ||
         hostInfo === Office.PlatformType.Universal; // Includes mobile web
}

export function supportsArchive(): boolean {
  if (typeof Office === 'undefined' || !Office.context?.requirements) {
    return false;
  }

  // getAsFileAsync lives in Mailbox 1.14
  return Office.context.requirements.isSetSupported("Mailbox", "1.14");
}

export function supportsMultiSelect(): boolean {
  if (typeof Office === 'undefined' || !Office.context?.requirements) {
    return false;
  }

  return Office.context.requirements.isSetSupported("Mailbox", "1.13");
}
```

**What's happening here?**

1. `isMobilePlatform()` asks the host directly what platform it's running on via `Office.context.diagnostics.platform`. Note that `Universal` covers mobile web too, that one surprised me.
2. `supportsArchive()` checks for Mailbox 1.14 before the code anywhere near `getAsFileAsync()` runs. On mobile this returns `false`, and the UI adapts instead of crashing.
3. `supportsMultiSelect()` does the same for multi-select (Mailbox 1.13). Desktop users get "archive 20 emails at once", mobile users get one at a time, and nobody gets an error.
4. Every function starts with a `typeof Office === 'undefined'` guard, so the same code doesn't explode if it runs outside Outlook (looking at you, local dev in a plain browser tab).

Then the components just ask:

```typescript
if (supportsArchive()) {
  // Full experience: grab the email with getAsFileAsync and upload it
} else {
  // Degraded experience: hide the feature, or take another route
}
```

The pattern scales nicely too. In my add-in, when `getAsFileAsync` isn't available on mobile, I don't hide the archive button, I fall back to fetching the message through Microsoft Graph instead. I wrote a follow-up post about exactly that: [Same Email, Different Source: Falling Back to Microsoft Graph on Outlook Mobile](/blogs/outlook-addin-graph-fallback-mobile/).

## The Contract (Write This on a Sticky Note)

> **Manifest floor = the lowest host you still want to appear in.**
> **Requirement sets in code = runtime capability checks, not install-time gates.**

The manifest decides *where you exist*. The code decides *what you can do once you're there*. Mixing those two up is how add-ins silently disappear from half your users' devices.

## Gotchas I Hit Along the Way

- **Your manifest can quietly use newer sets than it declares.** My manifest declares 1.5 but wires up an `OnMessageCompose` launch event, which needs Mailbox 1.12. That's fine, hosts simply ignore extension points they don't understand. But it means the manifest validator won't save you here; *you* have to know which parts light up where.
- **Mobile only gets what `MobileFormFactor` explicitly exposes.** No `MobileFormFactor`, no mobile button, even with a perfect 1.5 floor. And as of writing, mobile read mode is basically the only surface you get.
- **Test on an actual phone.** The desktop "mobile preview" and your imagination will both lie to you. Sideload it, open a real email on a real device, and watch what happens.

## Wrapping Up

Declaring `MinVersion="1.14"` because your code uses a 1.14 API feels correct, and it's exactly how you make your add-in invisible on mobile. Degrade the manifest to the lowest version your core experience needs, add a `MobileFormFactor`, and let `isSetSupported()` sort out the rest at runtime.

It felt like lying to the manifest at first. It's not, it's just answering the question the manifest is actually asking: *"What's the least you need to be useful?"*

## References 📚

- [Outlook add-in APIs and requirement sets](https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/apis)
- [Requirement sets supported by Exchange servers and Outlook clients](https://learn.microsoft.com/en-us/javascript/api/requirement-sets/outlook/outlook-api-requirement-sets)
- [Office.context.requirements.isSetSupported()](https://learn.microsoft.com/en-us/javascript/api/office/office.requirementsetsupport)
- [Add mobile support to an Outlook add-in](https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/add-mobile-support)
- [Outlook add-ins overview](https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/outlook-add-ins-overview)

