Microsoft 365のメールをMicrosoft Graph APIでバックアップ/リストアすると、復元したメールがすべて「下書き(Draft)」表示になり、送受信済みメールまで未送信扱いに見えてしまうことがあります。原因と仕様、SingleValueExtendedPropertiesを使った回避策、実装・検証の勘所をまとめます。
結論:Graph API単体では「非下書き状態」での完全復元はできない
まず押さえるべきポイントは、Microsoft Graph APIには「元が受信済み/送信済みのメールを、同じ状態のまま“復元”する」ための公式な直接手段が用意されていないことです。Microsoft側の回答でも、現時点ではnon-draft(非下書き)で復元するAPIは提供されていない旨が明確に述べられています。
さらに、Graphのメッセージ作成(POST /users/{id}/messages など)は、ドキュメント上も「新しいメッセージの下書きを作る」操作として説明されています。つまり「新規作成=下書き」という前提で設計されており、ここがバックアップ/リストア実装と衝突します。
| やりたいこと | Graph APIでの実現性 | 現実的な落としどころ |
|---|---|---|
| 受信済みメールを「受信済みのまま」復元 | 公式には困難(作成時点で下書き扱いになりやすい) | “表示・フラグ”を近づける(ワークアラウンド) |
| 送信済みメールを「送信済みのまま」復元 | 公式には困難(送信操作は実送信を伴う) | Sent Itemsへ格納+状態整形(ただし完全再現ではない) |
| 監査・コンプライアンス含め完全に同一のメタデータを維持 | Graph単体では要件を満たしにくい | 管理者向けの復元/移行手段も併用検討 |
なぜ「すべて下書き(Draft)」になるのか
Graph APIでメッセージを作成するエンドポイントは、基本的に「メールアイテムを“作成して保存する”」動作です。特に、Microsoft Learnの作成APIはdraft作成として定義されており、作成されたメッセージは下書き相当の状態を持ちます。
一方で、送信済みメールを作る操作としては「下書きを送る」API(POST /users/{id}/messages/{id}/send)が用意されていますが、これは実際に送信処理が走り、Sent Itemsに保存される前提のAPIです。バックアップからの復元でこれを実行すると、相手に再送されるリスクがあり、通常は使えません。
また、Graphの更新APIを見ると、subject や body など一部プロパティは isDraft=true の場合にしか更新できないと明記されており、「ドラフト状態」がGraphのメッセージ編集フローの中心にあることが分かります。
試しても改善しなかったことが多い典型パターン
同じ問題に直面した実装例では、次のような施策を試しても改善しなかったことが共有されています。
| 試したこと | 期待 | 改善しにくい理由(よくあるパターン) |
|---|---|---|
isDraft: false を指定して作成 | 下書き解除 | 作成フロー自体がdraft前提で、表示側(Outlook)が別フラグで判断することがある |
receivedDateTime / sentDateTime の設定 | 受信/送信時刻の復元 | サービス側が管理する属性で、意図どおり反映されない/反映されてもdraft表示が消えない |
| Inbox / Sent Items に作成、または Move で移動 | フォルダにより状態が変わる | フォルダだけでは「未送信」扱いの内部状態が変わらないことがある |
/beta の利用 | 新機能で改善 | /betaは本番利用非推奨で、仕様変更もあり得る(根本解決にならないことが多い) |
「復元のゴール」を先に定義するのが成功の近道
メールの復元要件は、開発側が思っている以上に段階があります。特に「ユーザー体験(見た目・混乱防止)」と「監査・真正性(完全同一性)」は、同じ“復元”という言葉でも目標が違います。
| ゴール | 重要視するポイント | Graph復元での現実的な狙い |
|---|---|---|
| ユーザーが混乱しない | Outlook上でDraft表示が出ない、受信/送信済みに見える | 拡張プロパティ等で“下書き相当のフラグ”を外す |
| 業務運用で困らない | 検索・スレッド・添付・既読未読が概ね期待どおり | フォルダ/既読/カテゴリ/件名整形などを含めて復元 |
| 監査・コンプライアンスまで厳密 | 真正性、ヘッダー、Message-ID、送受信経路の一致 | Graphだけに閉じない(管理者機能や移行手段の検討) |
公式対応:機能要望(Feature Request)を出す
Microsoft側からは「現時点で直接の方法はないため、機能要望を提出してほしい」という案内がされています。あわせて、/betaは変更され得るため本番利用は推奨されない注意も述べられています。
すぐに製品開発が必要な場合は次章のワークアラウンドを検討しつつ、中長期的には「公式の復元API」追加を要望しておくと、将来の保守コストを下げやすくなります。
回避策:SingleValueExtendedProperties(レガシー拡張プロパティ)で状態を整える
完全に“元通り”の送受信状態を保証するAPIがない一方で、実装上の回避としてSingleValueExtendedProperties(レガシー拡張プロパティ)を付与し、Outlookが参照するMAPI互換のフラグを調整して「下書き扱い」を外せた、という報告があります。
Graphはレガシー拡張プロパティ(singleValueLegacyExtendedProperty)をサポートしており、メッセージ作成時や更新時に埋め込めます。
報告例で使われたプロパティ(代表例)
以下は、実際に「Draft表示が消えた」報告で使われたプロパティの例です。なお、これはGraphの公式に保証された復元手段ではなく、クライアント表示や今後の挙動変更リスクがある点に注意してください。
| 拡張プロパティID | 型 | 例の値 | 狙い(意図) | 補足 |
|---|---|---|---|---|
String 0x001A | String | IPM.Note | メッセージクラスをメール(IPM.Note)として扱わせる | 0x001A は PidTagMessageClass(PR_MESSAGE_CLASS)として定義されます。 |
Integer 0x0E07 | Integer | 5 | メッセージフラグの調整(下書き相当の“未送信”フラグを外す狙い) | 0x0E07 は PidTagMessageFlags(PR_MESSAGE_FLAGS)。MSGFLAG_UNSENT は「まだ送られていない」状態を示します。 |
Integer 0x0E17 | Integer | 1 | 保存状態を“下書きではない”方向へ寄せる狙い | 0x0E17 は仕様上 PidTagMessageStatus(PR_MSG_STATUS / ptagMsgStatus)として定義されています。 |
Integer 0x6751 | Integer | 2 | 報告例では“送信済み”の方向へ寄せる狙い | 報告元では ptagSendStatus として扱われていますが、Graphでの効果は公式保証ではありません(採用するなら環境検証が必須)。 |
Integer 0x0FF7 | Integer | 1 | アクセスレベルの扱いを整える狙い | 0x0FF7 は PidTagAccessLevel(PR_ACCESS_LEVEL / ptagAccessLevel)として定義されています。 |
REST例:復元時に拡張プロパティを付与してメッセージを作成する
以下は考え方を掴むためのREST例です。ポイントは「作成時点で singleValueExtendedProperties を一緒に渡す」ことです(あとからPATCHしても、クライアントや保存状態によっては反映が限定的になる可能性があります)。
POST https://graph.microsoft.com/v1.0/users/{userId}/mailFolders/{folderId}/messages
Content-Type: application/json
{
"subject": "(復元)件名サンプル",
"body": {
"contentType": "html",
"content": "<p>本文(復元)</p>"
},
"toRecipients": [
{ "emailAddress": { "address": "[email protected]" } }
],
"singleValueExtendedProperties": [
{ "id": "String 0x001A", "value": "IPM.Note" },
{ "id": "Integer 0x0E07", "value": "5" },
{ "id": "Integer 0x0E17", "value": "1" },
{ "id": "Integer 0x6751", "value": "2" },
{ "id": "Integer 0x0FF7", "value": "1" }
]
}
このあと、添付ファイルがある場合はメッセージ作成後に添付を追加し、必要ならMoveで目的フォルダ(Inbox / Sent Items など)へ移す、という流れになります。
C#例:Microsoft Graph SDKでの作成イメージ
Graph SDKは世代によって型名やメソッド名が異なるため、ここでは「考え方」が伝わる形で示します(プロジェクトのSDKバージョンに合わせて読み替えてください)。
// newMessage はバックアップから復元したい内容を詰めた Message とする
var newMessage = new Message
{
Subject = originalSubject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = originalHtmlBody
},
ToRecipients = toRecipients,
CcRecipients = ccRecipients,
// 重要:SingleValueExtendedProperties を付与
SingleValueExtendedProperties = new List<SingleValueLegacyExtendedProperty>
{
new SingleValueLegacyExtendedProperty { Id = "String 0x001A", Value = "IPM.Note" },
new SingleValueLegacyExtendedProperty { Id = "Integer 0x0E07", Value = "5" },
new SingleValueLegacyExtendedProperty { Id = "Integer 0x0E17", Value = "1" },
new SingleValueLegacyExtendedProperty { Id = "Integer 0x6751", Value = "2" },
new SingleValueLegacyExtendedProperty { Id = "Integer 0x0FF7", Value = "1" },
}
};
// 例:特定フォルダへ作成(folderId は Inbox / Sent Items 等)
var created = await graphClient
.Users[userId]
.MailFolders[folderId]
.Messages
.PostAsync(newMessage);
// 添付があれば created.Id に対して attachments を追加
// その後、必要なら Move
実運用では「どのプロパティを復元対象にするか」「失敗時のロールバック」「スロットリング時のリトライ」なども含めた設計が重要です。
実装の勘所:復元フローを“固定”してブレを減らす
「Draft表示が消えたり戻ったり」「クライアントによって見え方が違う」といった不具合は、復元処理の順序が揺れると発生しやすくなります。以下のように手順を固定しておくと検証しやすく、トラブルシュートも早くなります。
| ステップ | 操作 | 狙い | 注意点 |
|---|---|---|---|
| メッセージ作成 | POST /mailFolders/{id}/messages | 本文・宛先・拡張プロパティをまとめて作る | 作成APIはdraft前提。拡張プロパティで“状態”を寄せる |
| 添付追加 | POST /messages/{id}/attachments | 添付を復元 | 大容量添付はアップロードセッション等の別手当が必要になる |
| 既読/未読の調整 | PATCH /messages/{id} | ユーザー体験を近づける | isReadは更新可能だが、他項目は draft制約があるので注意 |
| フォルダ移動 | POST /messages/{id}/move | 最終的な格納先を揃える | Moveしてもdraft表示が消えないケースがあるため、過信しない |
運用上の注意:これは“公式に保証された復元API”ではない
SingleValueExtendedPropertiesでの調整は、Graphに用意された正式な仕組み(拡張プロパティ)を使っている点では安全側ですが、「そのプロパティの組み合わせでOutlook表示がどう変わるか」までMicrosoftが保証しているわけではありません。実際にMicrosoft側の回答でも、Graph APIに直接の復元手段がないことが前提になっています。
- クライアント差:Outlook(Windows版/Mac版)、Outlook on the web、モバイルでDraft表示の判定が微妙に違う可能性があります。
- テナント差:Exchange Onlineの設定、保持ポリシー、監査設定などで挙動が変わることがあります。
- 将来変更:GraphやOutlookの更新で、同じMAPI互換プロパティが同様に効く保証はありません。
検証チェックリスト:公開前にここだけは見る
| 確認項目 | 見る場所 | 合格ラインの例 |
|---|---|---|
| 件名に [Draft] が付かない | Outlook(デスクトップ)一覧 | 復元後の一覧でDraft表記がない |
| 「This message hasn’t been sent」等が出ない | Outlook on the web | メッセージ詳細で未送信注意が出ない |
| 既読/未読が復元できる | 複数クライアント | isRead相当が期待どおり |
| 添付が欠落しない | 復元後のメッセージ | 添付数・サイズ・ファイル名が一致 |
| 検索でヒットする | Outlook検索 | 件名・本文キーワードで検索可能 |
要件が強い場合:Graphだけで完結させない選択肢も現実的
「監査のために完全一致が必要」「送信済みとして“事実”を保持したい」「メタデータの真正性が最優先」など要件が強い場合、Graph APIだけで無理に再現しようとすると、後から説明不能な差分が残ることがあります。
Microsoft側が“non-draft復元の直接手段はない”と明言している以上、Graphはバックアップ用途の一部として割り切り、次のような“管理者向け/移行向け”の仕組みも含めて検討すると安全です(具体的な最適解は、要件・権限・監査レベル・期間で変わります)。
| アプローチ | 向いているケース | 注意点 |
|---|---|---|
| Graph+表示整形(本記事のワークアラウンド) | ユーザーの混乱を避けたい/アプリ主導で復元したい | 将来変更・クライアント差のリスクがある |
| 管理者向けの復元・移行手段の併用 | 監査・真正性重視/大規模移行 | 権限・運用設計・コストが必要 |
| 復元メールを“別物”として明示(カテゴリ/件名プレフィックス等) | 完全一致より説明可能性を優先 | ユーザー教育が必要(ただし誤解は減る) |
よくある質問
Q. 「isDraft: false」を入れてもダメなのはなぜ?
A. Graphのメッセージ作成はドキュメント上もdraft作成であり、Outlookの表示は内部状態(メッセージフラグ等)に依存します。結果として、isDraftを意識しても表示が変わらないケースがあります。
Q. 送信済みにしたいなら /send を呼べばよい?
A. /send は「既存の下書きを送信する」APIです。Sent Itemsに保存されますが、通常は実送信が走るため、復元用途で呼ぶのは危険です。
Q. SingleValueExtendedPropertiesで“完全復元”できますか?
A. できるのは「表示・状態を近づける」ことです。Microsoftが保証する完全復元APIではないため、必ず検証環境でクライアント差も含めて確認し、運用要件に耐えるか判断してください。
まとめ:Graphの仕様を踏まえ、狙うべきは「完全一致」ではなく「説明可能な復元」
Microsoft Graph APIのメッセージ作成がdraft前提である以上、バックアップ/リストア実装で「送受信済みメールを完全に元どおりに戻す」ことは簡単ではありません。まずは“ユーザーが混乱しない”ところをゴールに置き、SingleValueExtendedPropertiesで下書き相当のフラグを外すワークアラウンドを検討するのが現実的です。
そのうえで、監査や真正性が要求される場合はGraphだけで抱え込まず、管理者向けの復元・移行手段も含めた設計に切り替えると、後からの説明責任や運用負債を減らせます。

コメント