Microsoft Graph APIのonlineMeetingでOrganizerをattendeesに含めたい問題の仕様と実務解決策【Teams会議】

Microsoft Graph API で Teams 会議(onlineMeeting)を作成するとき、「主催者(Organizer)を出席者(Attendees)にも入れておきたいのに、レスポンスから消える」という疑問は非常に多く寄せられます。本記事では、その挙動が“仕様”である理由と背景、現場で取るべき実装方針、代替アーキテクチャ、コード例、テスト観点までを一気通貫で解説します。

目次

Microsoft Graph API で onlineMeeting 作成時に「Organizer を attendees にも含めたい」問題の本質

Microsoft Graph API(v1.0)で onlineMeeting を作成する際、リクエストの participants.attendees 配列に主催者と同一のユーザー ID を入れても、レスポンスではそのユーザーが attendees から取り除かれます。この挙動はバグではなく、仕様どおりです。

  • 結論:participants は organizer(1 名固定)と attendees(それ以外)を厳密に分離するデータモデルで、重複は許容されません。
  • 影響:Organizer を Attendees として登録しようとしても、Graph サービス側で自動的に除外されます。
  • 回避策:API の設定やクエリで Organizer を attendees に強制的に残す手段は公開されていません。必要に応じてクライアント側で統合(マージ)するのが実務上の正解です。

データモデルの理解:participants の構造と分離の理由

onlineMeeting の participants は、次の 2 つの役割で構成されます。

プロパティ意味一意性典型的な利用
organizer会議を作成したユーザー(主催者)。常に 1 名。同一会議内で 1 名に限定(attendees と重複不可)。会議の所有・開催責任、既定の権限、管理操作の主体。
attendees主催者以外の参加者の配列。Organizer を含めない。重複は Graph により整理。招待・通知、出席管理、ロール(発表者/参加者)制御。

この分離により、クライアントは「会議の所有者(Organizer)」と「招待された参加者(Attendees)」を明確に判別でき、権限や UI の分岐(例:開催者のみが操作できるボタン)を設計しやすくなります。よって、Organizer を attendees に重複登録させないのは一貫した設計判断と言えます。

Outlook の event リソースとの違い

似たドメインである event(Outlook 予定表)では、実装やツールによっては Organizer が出席者としても扱われるケースがあります。一方、onlineMeeting は Teams 会議専用のデータモデルとして、Organizer と attendees が厳格に分離される点が大きな違いです。

観点onlineMeetingevent
主催者の扱いOrganizer は常に 1 名で、attendees に重複不可実装やクライアントによって Organizer が attendees に現れることもある
Teams 機能との結びつき会議権限、ロビー、発表者/参加者ロールなどが密結合主に予定表・通知・ICS 互換性に強い
ユースケース適正リアルタイム会議、管理・制御中心カレンダー運用・メール通知中心

要件が「とにかく 1 つの一覧に主催者も含めたい」で、Teams 会議リンク自体が必須でなければ、event を使い isOnlineMeeting=true を付与する設計も検討余地があります。ただし、Teams の詳細制御や会議管理 API をフル活用するなら onlineMeeting を選ぶのが基本です。

実際の挙動を確認する:リクエストとレスポンスの例

Organizer を attendees にも入れようとしたリクエスト例

POST https://graph.microsoft.com/v1.0/me/onlineMeetings
Content-Type: application/json

{
"subject": "仕様検証ミーティング",
"startDateTime": "2025-11-01T09:00:00Z",
"endDateTime": "2025-11-01T10:00:00Z",
"participants": {
"organizer": {
"identity": { "user": { "id": "11111111-1111-1111-1111-111111111111" } }
},
"attendees": [
{ "identity": { "user": { "id": "11111111-1111-1111-1111-111111111111" } } },  // Organizer と同一(重複)
{ "identity": { "user": { "id": "22222222-2222-2222-2222-222222222222" } } }
]
}
} 

レスポンス(抜粋):Organizer の重複は attendees から除外

{
  "id": "a1b2c3d4e5",
  "subject": "仕様検証ミーティング",
  "participants": {
    "organizer": {
      "identity": { "user": { "id": "11111111-1111-1111-1111-111111111111" } }
    },
    "attendees": [
      { "identity": { "user": { "id": "22222222-2222-2222-2222-222222222222" } } }
    ]
  }
}

ご覧のとおり、Organizer と同一 ID の要素は attendees から削除されます。これはサーバー側で「主催者は出席者に含めない」という仕様に基づき正規化されるためです。

公式に Organizer を attendees に残す方法はない:ではどう設計するか

API パラメータや追加設定で Organizer を attendees に強制的に残す方法は公開されていません。実務では次のいずれか、または複数を組み合わせます。

1. クライアント側でリストを統合する(最も手堅い)

取得した会議オブジェクトから organizer と attendees を結合し、「表示用」「集計用」など用途に応じて 1 本の配列を組み立てます。重複排除や並び順はクライアントの責務として明示します。

TypeScript 例(重複を ID で排除)

type Identity = { user?: { id?: string }, application?: any, device?: any };
type Participant = { identity: Identity };

function unifyParticipants(organizer: Participant, attendees: Participant[]): Participant[] {
const map = new Map();
const keyOf = (p: Participant) => p.identity?.user?.id ?? JSON.stringify(p.identity);
[organizer, ...attendees].forEach(p => {
const k = keyOf(p);
if (!map.has(k)) map.set(k, p);
});
return Array.from(map.values());
} 

C#(.NET SDK 連携を想定)

public record IdentityUser(string? Id);
public record Identity(IdentityUser? User);
public record Participant(Identity Identity);

public static IReadOnlyList UnifyParticipants(
Participant organizer, IEnumerable attendees)
{
var dict = new Dictionary();
string KeyOf(Participant p) => p.Identity.User?.Id ?? System.Text.Json.JsonSerializer.Serialize(p.Identity);
foreach (var p in new[] { organizer }.Concat(attendees))
{
var k = KeyOf(p);
if (!dict.ContainsKey(k)) dict[k] = p;
}
return dict.Values.ToList();
} 

SQL(監査・集計系での統一ビュー)

-- organizer と attendees 明細を正規化して UNION
SELECT meeting_id, user_id, 'organizer' AS role FROM meeting_organizers
UNION
SELECT meeting_id, user_id, 'attendee'  AS role FROM meeting_attendees;

2. 独自の「参加者管理テーブル」を持つ

BI/レポートで Organizer を含めた統一リストが必要な場合、アプリ側のデータストアで冪等ロジックを持ち、作成・更新のたびに正規化した「参加者スナップショット」を保存します。Analytics の時点で毎回 Graph を呼ばずに済むため、性能と一貫性が向上します。

// 例:participants_snapshot (meeting_id, user_id, roles, captured_at)
// roles: 'organizer', 'attendee', 'presenter' などの配列 or ビットフラグ

3. 「1 つのコレクション」にこだわる場合は event API を検討

要件が「カレンダー通知・メール互換性が最優先」で、Teams 会議のリアルタイム制御よりも予定表運用が中心なら、event リソースで作成し、isOnlineMeeting=true を付与するアプローチもあります。
ただし、Teams 固有の権限制御やロール管理を厳密に行いたい場合は onlineMeeting を選択し、クライアント側の統合で要件を満たすべきです。

ユースケース別:Organizer を含めた一覧が欲しくなる場面と対応

ユースケース課題推奨アプローチ
会議参加者一覧を UI に表示Organizer が attendees にいないと「主催者がいない」ように見えるフロントで organizer + attendees を統合して描画。Organizer はバッジ表示
出席率・稼働率の分析母集団に Organizer も含めたいETL で統合スナップショットを作成し、ロール別に集計
CSV/ICS 互換エクスポート他システムが単一リストを前提エクスポート直前に統合変換し、列に role を持たせる

設計のコツ:衝突しない ID・キーの選び方

  • 人の同一性は「ユーザー ID」を第一キーに。UPN(メール)は将来変わる可能性があるため補助キーに留める。
  • Graph の identity は user / application / device など複数種を取り得るため、比較ロジックは必ず種別を考慮。人以外(ボット等)が紛れるシナリオに備える。
  • 役割(role)は可変である前提で、同一ユーザーに複数ロールが付くモデルを許容する(例:Organizer かつ Presenter)。

API 実装レシピ:作成・取得・更新の基本

作成(Create)

POST https://graph.microsoft.com/v1.0/users/{organizerId}/onlineMeetings
{
  "subject": "プロジェクト定例",
  "startDateTime": "2025-11-05T08:00:00Z",
  "endDateTime":   "2025-11-05T09:00:00Z",
  "participants": {
    "organizer": { "identity": { "user": { "id": "{organizerId}" } } },
    "attendees": [
      { "identity": { "user": { "id": "{memberA}" } } },
      { "identity": { "user": { "id": "{memberB}" } } }
    ]
  }
}

取得(Get)

GET https://graph.microsoft.com/v1.0/me/onlineMeetings/{meetingId}?$select=id,subject,participants,startDateTime,endDateTime

更新(Patch)

参加者を入れ替える場合、Organizer を attendees に移動することはできません。Organizer を変更したい場合は「別のユーザーで新規作成」等の業務フローを検討します。

フロントエンドの UI パターン:ユーザー体験を壊さない工夫

  • 参加者リストは 「主催者」セクションと「出席者」セクションを分け、画面上で視覚的に統合する(例:1 つのリストに並べつつ、Organizer には「主催者」バッジ)。
  • 検索・フィルタリングはrole 別のトグルを提供し、Organizer の on/off を簡便に。
  • CSV 出力時は列に role を含め、取り込み先で柔軟に扱えるようにする。

テスト観点(回帰を防ぐためのチェックリスト)

  • Organizer と同一 ID を attendees に入れて作成 → レスポンスの attendees から除外されること。
  • 参加者統合関数(unify)に Organizer と attendees を渡した場合、重複が発生しないこと。
  • 異種 identity(ユーザー以外)が混在するケースで、誤マージがないこと(キー設計の妥当性)。
  • 時刻(タイムゾーン/DST)・ロケールの表記ゆれに耐えるフォーマット(ISO 8601)を維持。
  • 大規模会議(数百人)でも描画と検索が快適(仮想スクロール・インクリメンタル検索)。
  • リトライ・冪等性:連打やネットワーク断で二重登録が起きないこと。

トラブルシュート:はまりどころと対処

  • 「Organizer が attendees にいないのでおかしい」
    → 仕様どおり。UI/集計側で統合して見せる・出力する。
  • 「Organizer を別の人に差し替えたい」
    → 既存会議の Organizer を入れ替える API は想定されていない運用が多い。業務設計(再作成・引き継ぎ)で対応。
  • 「UPN で突合して重複扱いされない」
    → ID ベースで比較する。メール別名・変更に注意。
  • 429(レート制限)
    → バックオフとバッチングで緩和。統合スナップショットを用意して取得頻度を下げる。

セキュリティとコンプライアンスのメモ

  • 最小権限の原則:作成・取得・更新に必要なスコープのみを付与。
  • 個人情報(表示名・メール)はログに残さない/マスキングする。
  • 監査用の参加者スナップショットは暗号化し、保存期間・削除ポリシーを明文化。

実務で使える「サンプル変換」:役割付きの単一配列にまとめる

最終的に「1 本の配列」で扱いたい場合、アプリ内で次のように作ります。

// 入力(Graph の participants 相当)
const organizer = { identity: { user: { id: "111..." } }, role: "organizer" };
const attendees = [
  { identity: { user: { id: "222..." } }, role: "attendee" },
  { identity: { user: { id: "333..." } }, role: "attendee" }
];

// 出力(UI/CSV/分析で共通利用)
const unified = [
organizer,
...attendees
];
// 以降は unified を主たるデータモデルとし、表示や集計を一本化 

ポイントは、「Organizer を attendees に無理やり残す」のではなく、「用途上の単一配列をアプリ側で用意」することです。API の仕様と矛盾せず、拡張にも強いアプローチです。

アンチパターン

  • Organizer を attendees に入れるため、ID を一時的に書き換える(擬装する)…NG。本人同一性が崩れ、監査と整合性が壊れます。
  • レスポンスを書き換えて attendees に Organizer を追加して保存する…NG。後続処理が「API の真実」と乖離し、不具合温床になります。
  • UPN のみで同一判定…非推奨。将来のメール別名・ドメイン統合で破綻します。

まとめ:要点の再確認

  • onlineMeeting は Organizer と Attendees が厳密に分離される仕様。Organizer を attendees に重複登録しても Graph が除外します。
  • 公式に重複を許容するオプションはないため、必要なら「取得後に統合」する設計を採用します。
  • 予定表中心なら event + isOnlineMeeting=true も検討。ただし Teams の細かな会議制御が主眼なら onlineMeeting が基本。
  • 同一性はユーザー ID を第一キーに。ロールは可変で、単一配列の表示・集計はアプリ側で整えましょう。

付録:現場でそのまま使えるチェックリスト

  • [仕様理解]Organizer は attendees に含めない(Graph が除外)。
  • [UI 設計]表示用は organizer + attendees を統合、Organizer にバッジ。
  • [データ設計]ユーザー ID を主キー、UPN は補助。ロールは多値許容。
  • [分析設計]統合スナップショット(冪等)を定期作成。
  • [テスト]重複除外・異種 identity 混在・大人数・429 を網羅。
  • [運用]権限は最小限、PII はマスク、保存期間を明文化。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次