ACS Call Automation playToAll silent in Teams Online Meeting (CallConnected + PlayCompleted, list_participants=0)
- Dominant language
- No language data
- Stars
- 340
- Forks
- 113
- PR merge metrics
- No merged PRs in 30d
Description
# Microsoft Azure Support Ticket — Teams Bidirectional Media (Voice + Live Avatar)
**Stand:** 2026-06-30
**Sprache Ticket:** Englisch (Copy-Paste unten)
**Portal:** [Azure Portal](https://portal.azure.com/) → Help + support → Create a support request
**Schritt-für-Schritt (Deutsch):** [`MICROSOFT_SUPPORT_TICKET_PORTAL_WALKTHROUGH_2026-06-30.md`](MICROSOFT_SUPPORT_TICKET_PORTAL_WALKTHROUGH_2026-06-30.md)
**Issue type:** Technical → **Azure Communication Services** (primary) + cross-reference **Microsoft Teams / Graph Cloud Communications**
---
## Kurz (Deutsch, intern)
Wir bauen **STELLA** — einen KI-Copilot für Teams-Meetings mit **bidirektionaler** Kommunikation:
| Phase | Ziel |
|-------|------|
| **1 — Voice only** | STELLA spricht **hörbar** in der Meeting-Leitung (TTS), hört Teilnehmer (STT), antwortet intelligent |
| **2 — Voice + Video** | Live-Avatar (Runway) als **sichtbare Kachel** im Meeting-Grid, synchron zu Audio |
**Blocker:** ACS Call Automation (`playToAll`) meldet `CallConnected` + `PlayCompleted`, aber `list_participants=0` → **kein hörbares Audio** (Ghost Leg). Das bleibt bestehen **auch nach** native `onlineMeetings`-Erstellung + Application Access Policy.
**Bitte an Microsoft:** Empfohlener Architekturpfad für produktionsreife **Bot-Media in Teams** (Audio rein/raus, später Video) — ACS vs Application-hosted Media vs andere.
---
## Copy-Paste: Support Request (English)
### Title
```
ACS Call Automation playToAll silent in Teams Online Meeting (PlayCompleted, participants=0) — guidance for bidirectional voice + live avatar video
```
### Problem description
We are building **STELLA**, a multi-tenant AI meeting copilot integrated with **Microsoft Teams**. Our production stack uses:
- **Azure Communication Services (ACS)** resource: `cdtech-communication` (region: **Europe**)
- **Microsoft 365 tenant:** `fe87c2f4-700c-4a68-9bd7-218fde87a857` (C&D Technologies GmbH)
- **Organizer UPN:** `mj@ilovestella.de` (also `mj@cdtechnologies.de`)
- **Backend:** Python/FastAPI on Azure Railway (`https://api.cdtechnologies.de`)
- **Teams Graph App (application permissions):** Client ID `4b1d32ba-6c39-4a0b-b320-31981ce0654b` (`GRAPH_CLIENT_ID_TEAMS`)
We need **guidance on the correct Microsoft-recommended architecture** for:
1. **Outbound audio (Phase 1):** Bot TTS audible to **all meeting participants** (not operator-only Sidecar/browser audio).
2. **Inbound audio (Phase 1):** Bot receives participant speech for STT / conversational AI (**bidirectional voice**).
3. **Outbound video (Phase 2):** Live AI avatar (H.264) as a **visible participant tile** in the Teams meeting grid, lip-synced to TTS.
4. **Scalable multi-tenant SaaS:** Each customer uses their own M365 tenant; we orchestrate via delegated/app permissions and tenant-specific organizer mailboxes.
---
### Current architecture (what we built)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ STELLA Backend (Railway, Linux, Python) │
│ • Meeting create: Graph POST /users/{org}/onlineMeetings (native) │
│ • Copilot session, TTS (Azure Speech de-DE-KatjaNeural → WAV) │
│ • ACS Call Automation: connect_call(groupCallId) + playToAll(audioUrl) │
│ • Callbacks: POST /api/teams/copilot-acs-media/events │
│ • Bot Framework: meeting chat text (works) │
│ • Sidecar UI: browser STT chunks → backend (works, not meeting-wide) │
└───────────────┬───────────────────────────────┬─────────────────────────────┘
│ HTTPS │ Graph / Bot Framework
▼ ▼
┌───────────────────────────┐ ┌────────────────────────────────────────────┐
│ ACS (cdtech-communication)│ │ Teams Online Meeting │
│ Call Automation API │───▶│ groupCall / thread v2 │
│ (invisible bot leg) │ │ Human participants + meeting chat │
└───────────────────────────┘ └────────────────────────────────────────────┘
Optional (scaffold, Windows VM — not production-ready):
┌─────────────────────────────────────────────────────────────────────────────┐
│ teams-media-bot (.NET 8, Azure Windows VM) │
│ • Target: Microsoft.Graph.Communications.Calls.Media (Application-hosted) │
│ • StellaBridge HTTP: push PCM + H.264 frames │
│ • Today: ACS relay fallback only; GraphCommunicationsService = deferred │
│ • URL: https://teams-media-bot.cdtechnologies.de (health often timeout) │
└─────────────────────────────────────────────────────────────────────────────┘
Live avatar source (Phase 2):
┌─────────────────────────────────────────────────────────────────────────────┐
│ Runway Live API → WebRTC/video frames → transcode → Media Bot VideoSocket │
│ (Sidecar bridge today: operator-only video; not in Teams grid) │
└─────────────────────────────────────────────────────────────────────────────┘
```
**Separate channels (important):**
| Channel | Technology | Status |
|---------|------------|--------|
| Meeting **chat** (text, @mentions) | Bot Framework + Graph | ✅ Works |
| Meeting **audio broadcast** (TTS to all) | ACS `playToAll` | ❌ Ghost Leg — silent |
| Operator **Sidecar** TTS | Browser audio | ✅ Operator hears only |
| **Transcript** post-meeting | Graph onlineMeeting transcript | ✅ (after meeting ends) |
| Live **STT from all participants** | Not implemented | ❌ Need inbound media path |
| **Visible avatar tile** | Graph Application-hosted media (planned) | ❌ Not wired |
---
### Symptom (Ghost Leg)
When joining a **native Teams online meeting** (created via Graph `onlineMeetings`, not calendar fallback):
- ACS `connect_call` succeeds; webhook `CallConnected` received.
- ACS `playToAll` with publicly reachable HTTPS WAV URL succeeds; webhook `PlayCompleted` received.
- ACS `list_participants` returns **`count=0`** consistently after connect.
- **No audible media** in Teams client for human participants.
- Sidecar/browser TTS works for operator only (not acceptable for product).
**Application Access Policy** (Teams PowerShell) already applied:
- Policy: `Tag:STELLA-Graph-OnlineMeetings` → AppId `4b1d32ba-6c39-4a0b-b320-31981ce0654b`
- Granted to: `mj@ilovestella.de`, `mj@cdtechnologies.de`, `mc@ilovestella.de`
- Meeting create returns `_source: onlineMeetings` (MS… meeting ID) — **Ghost Leg persists**.
---
### Reproduction evidence (2026-06-30, Policy Test)
| Field | Value |
|-------|-------|
| Meeting subject | `STELLA onlineMeetings Policy Test 2026-06-30` |
| onlineMeeting ID | `MSplYzJiODRlNy01MTJlLTQyMzUtOTYzYi01YjJlOTA0MjNhNmMqMCoqMTk6bWVldGluZ19OalpqTkdVek5HVXROVEkxT1MwMFlXRTVMVGt3Wm1FdE1HSTFNbUZpWlRZek1HRTFAdGhyZWFkLnYy` |
| groupCallId (thread) | `66c4e34e-5259-4aa9-90fa-0b52abe630a5` |
| ACS call_connection_id | `6d003a80-a129-48b2-a1ec-22e979b71feb` |
| Join URL thread | `19:meeting_NjZjNGUzNGUtNTI1OS00YWE5LTkwZmEtMGI1MmFiZTYzMGE1@thread.v2` |
| Callback URL | `https://api.cdtechnologies.de/api/teams/copilot-acs-media/events` |
| Backend flags | `TEAMS_COPILOT_AUDIO_PREFER_RAILWAY_ACS=true`, Runway broadcast **off** |
| Human in call | Yes (Teams desktop, mic bar active, second machine) |
| Result | `PlayCompleted`, **no audible output** in meeting |
Earlier retest (calendar fallback, same symptom):
- groupCallId: `e6613890-2b4e-4b2d-80b9-1b716e146e36`
- ACS call: `1e003a80-2f9a-4348-a143-3b8206013c61`
---
### Graph / Entra permissions (Teams app `4b1d32ba…`)
Application permissions (admin consented), including:
- `Calls.JoinGroupCall.All`, `Calls.InitiateGroupCall.All`
- `OnlineMeetings.ReadWrite.All`, `OnlineMeetings.Read.All`
- `Calendars.ReadWrite`, `Calendars.Read`
- `Chat.ReadWrite.All`, `TeamsAppInstallation.ReadWriteForChat.All` (auto-install still 403 without Consent variant)
- `User.Read.All`, `CallRecords.Read.All`, etc.
**Bot Framework app** (separate registration) handles `/api/teams/bot/messages` — chat works; does not provide meeting media.
---
### What we need from Microsoft
Please advise on the **recommended production path** for our goals:
#### A) Outbound meeting audio (Phase 1 — voice only)
1. Is **ACS Call Automation `playToAll`** into **Teams native online meetings** a supported scenario? If yes, why would `list_participants=0` + silent audio occur despite `CallConnected`/`PlayCompleted`?
2. Should we use **Graph Cloud Communications** / **Application-hosted media bot** instead (Windows VM + `Calls.AccessMedia.All` + media platform certificate)?
3. Is there a **service-hosted media bot** or **Teams Real-time API** path that avoids Windows VM ops but still delivers audible mixed audio?
4. Any tenant-level enablement required beyond Application Access Policy (e.g. Cloud Communications error **7504**, policy flags)?
#### B) Inbound audio — bidirectional voice (Phase 1)
5. How should a bot **receive** participant audio for STT in a Teams meeting?
- Graph `AudioSocket` / subscribed media?
- ACS receive path?
- Teams transcription API (too high latency for live dialog)?
6. Minimum architecture for **full-duplex voice** (human speaks → bot hears → bot responds with TTS in meeting)?
#### C) Outbound video — live avatar (Phase 2)
7. Recommended approach to inject **live H.264 video** (AI avatar, ~25 fps) as a **visible meeting participant**:
- Application-hosted media only?
- Can ACS carry video into Teams grid or audio-only?
8. Can **audio and video** use the same Graph call leg, or must they be separate?
9. Licensing / SKU requirements (Teams Premium, Copilot, etc.)?
#### D) Multi-tenant SaaS
10. For **customer tenants** (not our own): recommended onboarding — Application Access Policy per tenant, consent model, hosted vs customer-deployed media bot?
11. Any **certification** or **Teams store** requirements for a copilot with media injection?
---
### Alternatives we are evaluating (please comment)
| Option | Our understanding | Question for Microsoft |
|--------|-------------------|-------------------------|
| **ACS Call Automation** | Simple HTTP, Linux-friendly; current Ghost Leg | Supported for Teams meetings? Fix for participants=0? |
| **Application-hosted Media Bot** | Windows VM, Graph Communications SDK, AudioSocket/VideoSocket | Is this the only supported path for grid video + reliable audio? |
| **Service-hosted media bot** | Microsoft hosts media stack | Available for our scenario / tenant size? |
| **Teams RTMP / live events** | Not interactive meeting | Not suitable — need 2-way |
| **Screen share / computer sound** | Operator workaround only | Not productizable |
| **Azure OpenAI Realtime + ACS** | Voice AI patterns | Overlap with Teams meeting join? |
---
### Business context
- **Product:** STELLA — AI copilot for regulated industries (energy consulting, sales meetings).
- **Use case:** AI assistant joins customer Teams meetings, introduces itself (ISB compliance script), answers questions **audibly**, later with **live avatar**.
- **Requirement:** Bidirectional communication; voice-first MVP, video avatar second phase.
- **Scale:** Multi-tenant; each customer brings their own M365 tenant.
---
### Severity / impact
- **Severity:** High — blocks core product capability (audible AI in Teams meetings).
- **Workaround:** Operator shares "computer sound" from browser Sidecar — not acceptable for customers.
---
### Attachments / logs we can provide on request
- ACS webhook event sequence (CallConnected, PlayCompleted timestamps)
- Redacted Railway logs for call_connection_id above
- Graph meeting create response (`onlineMeetings`)
- Architecture ADR: `docs/architecture/TEAMS_LIVE_AVATAR_MEDIA_BOT_DECISION_2026-06.md` (private repo — summary included above)
---
### Contact
- **Primary:** Markus Jaszczyk — `mj@ilovestella.de` / `mj@cdtechnologies.de`
- **Organization:** C&D Technologies GmbH
- **Tenant ID:** `fe87c2f4-700c-4a68-9bd7-218fde87a857`
---
## Nach dem Absenden
1. **Correlation IDs** aus Azure Portal notieren und in `docs/ops/TEAMS_ACS_GHOST_LEG_INCIDENT_2026-06-30.md` ergänzen.
2. Antwort mit Architektur-Empfehlung → ADR aktualisieren + P1 Media Bot Scope anpassen.
3. Falls **7504 / tenant enablement** nötig: separates Enablement-Ticket referenzieren.
## Verwandte interne Docs
- [`TEAMS_ACS_GHOST_LEG_INCIDENT_2026-06-30.md`](TEAMS_ACS_GHOST_LEG_INCIDENT_2026-06-30.md)
- [`TEAMS_GRAPH_MEDIA_BOT_P1_HANDOFF_2026-06-30.md`](TEAMS_GRAPH_MEDIA_BOT_P1_HANDOFF_2026-06-30.md)
- [`TEAMS_VOICE_AUDIO_TEST_HANDOVER_2026-06-29.md`](TEAMS_VOICE_AUDIO_TEST_HANDOVER_2026-06-29.md)
- [`services/teams-media-bot/README.md`](../../services/teams-media-bot/README.md)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the documented ACS callback endpoint `/api/teams/copilot-acs-media/events` and the reproduction evidence for `CallConnected`, `PlayCompleted`, and `list_participants=0`. Review `MICROSOFT_SUPPORT_TICKET_PORTAL_WALKTHROUGH_2026-06-30.md` and the referenced architecture ADR summary. Done means an agreed, supported production architecture for bidirectional Teams audio and later video, rather than a repository code change.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, fastapi, python
- Domain
- audio-video-rtc, backend, cloud
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100