Recordings & Transcripts - Operator Manual
9 min read
Enabling Recordings & Transcripts means configuring two systems: the Teams MCP Server, which captures meeting transcripts into the knowledge base, and the Unique platform, which presents them in the Recordings area. Both must point at the same knowledge-base root scope — that single shared value is the most common source of misconfiguration.
Read the eligibility section first
This feature stores copies of meeting transcripts and recordings in the Unique knowledge base. Confirm that this is acceptable to your organisation before enabling it — see Is this feature for you?.
Prerequisites
Prerequisite | Details |
|---|---|
Teams MCP Server deployed | |
RabbitMQ | Required for the ingestion pipeline; chat-only deployments do not need it |
Admin consent granted |
|
Zitadel service account | With the roles listed below — see Zitadel service account |
Root scope created | Created manually in Unique before deployment — see Root scope |
Teams transcription enabled | By Microsoft Teams meeting policy, otherwise there is nothing to capture |
Grant admin consent
Transcript capture needs two Microsoft Graph scopes that Microsoft classes as privileged, because they read meeting content: OnlineMeetingTranscript.Read.All and OnlineMeetingRecording.Read.All. Both require admin consent — users cannot approve them for themselves. Until an administrator has granted it, users see an error when they try to connect.
Capture runs on a different Entra ID app registration than a chat-only Teams MCP deployment, so consent granted for the chat-only app does not cover this feature. Use the URL below.
Recommended for most clients. When Unique runs Teams MCP with capture enabled, Unique provisions the Entra ID app registration for you. You only need to grant admin consent:
https://login.microsoftonline.com/organizations/adminconsent?client_id=8ddffb12-1579-4fa8-8844-ca122e4308bcThe consent prompt lists every scope the capture-enabled server requests — the chat and messaging scopes plus Calendars.Read, OnlineMeetings.Read, and the two meeting-content scopes above. The URL handles them in one step. For the full list with least-privilege justification, see Teams MCP - Permissions.
If your organization uses multiple Azure tenants, confirm you are granting consent for the correct directory. See Grant tenant-wide admin consent to an application for a tenant-specific admin consent URL; use application (client) ID .
Self-hosted deployments provision their own app registration instead — see Teams MCP - Authentication, and add the two meeting-content scopes to it.
Enable ingestion on the Teams MCP Server
Set UNIQUE_INTEGRATION=enabled. This registers the transcript ingestion pipeline and the four knowledge-base tools, and makes the Unique API and Zitadel configuration mandatory.
Variable | Helm Path | Default | Description |
|---|---|---|---|
|
| (required) | Set to |
|
|
|
|
|
| (required) | Unique API endpoint |
|
|
| API version |
|
| (required) | Root scope all meetings are ingested under. Must match |
|
|
| Parallel participant lookups |
|
| (required with | Ingestion service endpoint |
|
| (required) | Zitadel service account headers ( |
|
|
| When enabled, every user gets a transcript subscription at login instead of having to call |
Subscription and webhook behaviour is configured with the following variables. Their lifecycle is described in the Technical Manual.
Variable | Default | Description |
|---|---|---|
|
| Hour of day (UTC, 0–23) at which scheduled subscription expirations are set. Choose an off-peak hour to avoid disrupting incoming notifications |
| (required) | Secret sent as the subscription |
|
| Public URL Microsoft Graph posts notifications to |
For the full Teams MCP variable reference, including the variables that are shared with chat-only deployments, see Teams MCP - Configuration.
Example Helm values
mcpConfig:
unique:
integration: enabled
serviceAuthMode: cluster_local
apiBaseUrl: http://api-gateway.unique:8080
apiVersion: "2023-12-06"
rootScopeId: scope_abc123xyz # must equal RECORDING_KB_SCOPE_ID
userFetchConcurrency: 5
ingestionServiceBaseUrl: http://node-ingestion.unique:8091
serviceExtraHeaders:
x-company-id: "<your-company-id>"
x-user-id: "<your-service-account-user-id>"For deployments outside the Unique cluster, use serviceAuthMode: external with an API key:
mcpConfig:
unique:
serviceAuthMode: external
apiBaseUrl: https://api.unique.app
serviceExtraHeaders:
authorization: "Bearer <api-key>"
x-app-id: "app-id"
x-user-id: "user-id"
x-company-id: "company-id"Zitadel service account
Why it is required
Ingestion happens on behalf of the server, not the end user, so the Teams MCP Server needs its own identity against the Unique Public API. The service account is used to:
Resolve participants — look up meeting attendees in Unique by email or username
Create scopes — create the folder structure that holds each meeting
Set access permissions — grant the organiser and participants their access
Upload content — write the transcript and recording into the knowledge base
Credentials are passed as the x-company-id and x-user-id headers on every API request.
Creating the service account
Log in to Zitadel and select the organisation where meetings should be ingested
Create the service account — under Service Accounts, click New Service Account and give it a descriptive name (e.g. "Teams MCP Server Service Account")
Note the identifiers — the organisation ID becomes
x-company-id, the service account user ID becomesx-user-idConfigure the headers in
mcpConfig.unique.serviceExtraHeadersas shown above
Required roles
The Unique Public API authorises each request against the Zitadel roles of the calling identity. Grant both of the following roles to the service account in the target organisation:
Role | Why it is required |
|---|---|
| Mandatory. The only role accepted when updating a scope (setting its |
| Mandatory. Required to query existing content ( |
Each operation maps to a downstream role check as follows:
Operation | Endpoint | Accepted roles |
|---|---|---|
Resolve meeting participant |
|
|
Create meeting scope |
| Service-identity only (no user role required) |
Grant participant scope access |
|
|
Set scope |
|
|
Upsert transcript / recording content |
|
|
Query existing content |
|
|
Search content |
| None (header auth only) |
Upload blob |
| None (secured by encrypted key) |
Participants need their own roles
When the server grants scope access to a meeting participant, Unique additionally requires that participant's own Zitadel account to already hold chat.knowledge.write (or chat.admin.all) for write access, or chat.knowledge.read for read access. A participant with no roles assigned in Zitadel is rejected. This is a property of the users being granted access, not of the service account.
Role enforcement toggle
Downstream role checks are only enforced when unique-api runs with ENABLE_ROLE_AUTHORIZATION=true (the platform default). When disabled, unique-api calls downstream services with a service identity that bypasses these checks, and the roles above are not required.
Root scope
Why it is required
All meetings are ingested as child scopes under one root scope (folder), giving a single organisational entry point for Teams content and one place to manage its permissions and visibility. The Recordings area in Unique reads from exactly this scope.
Creating the root scope
The root scope must be created manually in the Unique platform before deploying — there is no automated provisioning for this step.
Log in to the Unique platform as an administrator
Create a new top-level scope (folder) with a descriptive name (e.g. "Teams Recordings") and note its ID — it starts with
scope_Grant the service account access — give the Zitadel service account
MANAGE,READ, andWRITEon the root scope. This is a required manual step: the server cannot provision access on a scope it has no rights toConfigure both systems with the scope ID —
UNIQUE_ROOT_SCOPE_IDon Teams MCP andRECORDING_KB_SCOPE_IDon the platform
On startup the server re-issues these three grants for its service user against the configured root scope. The call is additive, so re-affirming existing access on every boot is safe. If it is rejected — a wrong or missing scope ID, or a service account that was never granted management of the scope — the pod hard-fails at boot rather than surfacing the error mid-ingestion. Look for root scope <id> permission bootstrap failed in the startup logs.
One scope per environment
Each environment (QA, production, …) requires its own root scope. Do not reuse a scope ID across environments.
Enable the Recordings area in Unique
The UI is gated by a feature flag and needs to know which scope to read from.
Setting | Required | Description |
|---|---|---|
| Yes | Unlocks the Recordings area: list, detail, participants, reports, and sharing. Disabled by default; while it is off, the navigation entry is hidden and the route redirects away |
| Yes | The knowledge-base scope the Recordings area reads from. Must be the same value as |
| No | The assistant space used by the Open chat action on the recording detail view. Without it, users can still browse and play recordings |
The two scope IDs must match
If RECORDING_KB_SCOPE_ID and UNIQUE_ROOT_SCOPE_ID differ, ingestion will succeed and the Recordings area will stay permanently empty — with no error anywhere. Verify both values whenever the list is unexpectedly empty.
For Unique SaaS deployments, Unique applies the flag and both settings for you; confirm the root scope with your Unique contact.
Enablement checklist
Knowledge base
1incomplete Root scope created in Unique, scope ID noted2incomplete Zitadel service account created withchat.admin.allandchat.knowledge.read3incomplete Service account grantedMANAGE,READ,WRITEon the root scopeMicrosoft Entra ID
1incompleteOnlineMeetingTranscript.Read.AllandOnlineMeetingRecording.Read.Alladded2incomplete Admin consent granted for both — see Grant admin consent3incomplete Teams meeting policy has transcription enabledTeams MCP Server
1incomplete RabbitMQ reachable2incompleteUNIQUE_INTEGRATION=enabledwith the Unique API and service header values set3incompleteUNIQUE_ROOT_SCOPE_IDset to the root scope4incomplete Pod starts withoutroot scope … permission bootstrap failedUnique platform
1incompleteFEATURE_FLAG_ENABLE_RECORDING_UN_19218enabled2incompleteRECORDING_KB_SCOPE_IDequalsUNIQUE_ROOT_SCOPE_ID3incompleteRECORDING_CHAT_SPACE_IDset if the Open chat action is wantedVerification
1incomplete A test user connects and callsverify_kb_integration_status, which reportsactive2incomplete A recorded, transcribed test meeting appears in the knowledge base under the root scope3incomplete The same meeting appears in the Recordings area, plays back, and shows its transcript
Troubleshooting
Symptom | Likely cause | What to check |
|---|---|---|
Recordings area is not in the navigation | Feature flag off |
|
Recordings area is empty, but content exists in the knowledge base | Scope mismatch |
|
Nothing is captured at all | No active subscription |
|
Nothing is captured for one user | That user never enabled ingestion, or their token expired | Have the user reconnect, then call |
Capture stopped after a few days | Subscription renewal failed | Logs for |
Meeting appears without video | Meeting was not recorded, or the recording download timed out | Ingestion logs for the recording step; the transcript is captured either way |
Transcript present, participants have no access | Participants do not resolve to Unique accounts, or lack Zitadel roles | Participant emails against Unique accounts; see the participant roles note above |
Pod fails to start | Root scope missing or service account lacks access on it |
|
Meetings stop being captured for everyone | Webhook secret changed | Rotating |
Related Documentation
Recordings & Transcripts — what the feature is and who it is for
Technical Manual — architecture, ingestion pipeline, subscription lifecycle, and tools
FAQ — frequently asked questions
Teams MCP - Operator Manual — deploying and operating the server itself
Teams MCP - Configuration — the full environment variable reference
Teams MCP - Authentication — Entra ID app registration and admin consent