Connecting Microsoft Teams
EduBook captures attendance from Teams meetings via Microsoft Graph subscriptions. Once connected, every join/leave event from a Teams meeting bound to one of your groups gets recorded; students who stay above the configured threshold are auto-marked present.
This works alongside Zoom — a tenant can have both connected at once and pick the platform per group.
Before you start
You need:
- A Microsoft 365 account with admin access to your tenant (the connect flow requires granting admin consent).
- 15–20 minutes for the Azure app registration setup.
The flow looks more involved than Zoom’s because Microsoft splits “create an app” and “give the app permission to do anything” across two screens, both of which need the admin to confirm.
1. Create an Azure AD app registration
- Sign in to entra.microsoft.com as a global admin.
- Side nav → Identity → Applications → App registrations → New registration.
- Name:
EduBook Attendance - Supported account types: pick Accounts in this organizational directory only (single tenant).
- Redirect URI: leave blank — we use the client_credentials flow which has no redirect.
- Click Register.
You’re now on the app’s overview page. Copy two values you’ll paste into EduBook later:
- Directory (tenant) ID
- Application (client) ID
2. Add the API permission
- Side nav of the app → API permissions.
- Add a permission → Microsoft Graph → Application permissions (NOT “Delegated”).
- Search for
CallRecords.Read.All→ check it → Add permissions. - Grant admin consent for {your tenant} — this is the screen most setup mistakes happen on. The button only appears for users who can grant tenant-wide consent (global admin, app admin, or cloud app admin). If you don’t see it, hand the page to someone who does.
- Wait for the green ✓ next to the permission. Status should read “Granted for {your tenant}”.
Without this step nothing works — Microsoft will let you create a subscription but no events will ever fire.
3. Create a client secret
- Side nav → Certificates & secrets → Client secrets tab → New client secret.
- Description:
EduBook(or anything memorable). - Expires: pick a duration. Microsoft caps client secrets at 24 months. Set a calendar reminder to rotate the secret 1 month before expiry — when it expires, EduBook stops working and you’ll need to paste a new one.
- Click Add.
- IMMEDIATELY copy the “Value” column. Microsoft only shows it once. The “Secret ID” column is NOT what you want.
4. Connect from EduBook
In EduBook → Settings → Integrations → Microsoft Teams:
- Paste Microsoft tenant ID (from step 1).
- Paste Application (Client) ID (from step 1).
- Paste Client Secret (the Value from step 3).
- Click Connect Teams.
EduBook does three things on save:
- Calls Microsoft’s
/oauth/tokenendpoint to validate the credentials. Wrong values = visible error, nothing persisted. - Generates a per-tenant
clientStatesecret (used to authenticate every inbound webhook from Microsoft). - Calls Microsoft Graph to create a
communications/callRecordssubscription pointing at our webhook endpoint.
If any of those fail you’ll see the error in the toast. The most common failure is subscription creation rejected because admin consent wasn’t granted in step 2.
On success the card flips to Connected ✓ and shows your tenant ID + the subscription’s expiration timestamp.
5. Use it
Create an online group, pick Microsoft Teams as the platform,
and paste the Teams meeting join URL (the
https://teams.microsoft.com/l/meetup-join/... URL students click
to join).
When students join the meeting:
- Each join/leave is recorded in EduBook.
- Cumulative time is summed (default 30-minute threshold; per-group editable).
- When a student crosses the threshold, they’re marked present with
source: teamsin the attendance log.
Matching is by Microsoft account display name + Azure user id. For reliable matching, ask students to join Teams signed in with the same Microsoft account email you have on file.
How attendance differs from Zoom
| Zoom | Teams | |
|---|---|---|
| Match by email | ✅ | ⚠️ (limited — see below) |
| Match by phone | ✅ | ✅ |
| Webhook signing | HMAC-SHA256 in header | clientState in body |
| Subscription lifetime | Forever (in Zoom marketplace) | ~3 days (auto-renewed) |
| Auto-create meetings | ✅ Enterprise | ❌ (manual paste only) |
The email-match limitation: Microsoft Graph’s callRecord events
identify participants by Azure user object id, not email. Resolving
the email requires a separate /users/{id} lookup that we skip
in v1 to keep latency low. So if a student joins Teams from a
personal Microsoft account that doesn’t share an email with their
EduBook record, the auto-match misses. Workarounds:
- Encourage students to use the work/school account that has the email matching their EduBook record.
- Or ask them to dial in via PSTN — phone-number matching uses the number Microsoft surfaces on the phone identity.
Auto-create Teams meetings is on the roadmap but requires delegated permissions (a per-user OAuth flow) instead of application permissions. Today, Pro/Enterprise tenants paste the meeting URL manually — same as the rest of the flow.
Daily renewal
Microsoft caps callRecord subscriptions at 4230 minutes (~3 days). EduBook’s renewal cron runs once a day, finds subscriptions with less than ~24 hours of life left, and calls Graph’s PATCH endpoint to push the expiration forward. You don’t need to touch this.
If the renewal ever fails (most often: client secret expired), the integration card surfaces the error in Settings → Integrations → Microsoft Teams → Last error. Fix the underlying cause (rotate the secret) and click Reconnect to recreate the subscription.
Disconnecting
Settings → Integrations → Microsoft Teams → Disconnect does two things:
- Calls Microsoft Graph to DELETE the subscription so events stop arriving.
- Erases the credentials from EduBook’s database.
The Azure AD app registration on entra.microsoft.com is untouched — re-connect anytime by pasting the same credentials.
Troubleshooting
“Microsoft rejected those credentials” — check the three values for typos. The “Value” column from Certificates & secrets is what you want, not the Secret ID.
“Microsoft Graph rejected the subscription request” — admin
consent for CallRecords.Read.All wasn’t granted (or was revoked).
Re-do step 2 → Grant admin consent.
Connected but “Last event received” stays empty — make sure
your group’s Meeting URL is the EXACT join URL Microsoft
generates (not a tinyurl, not a Teams calendar link with extra
parameters). The webhook matches groups by joinWebUrl exact
string match.
Auto-attendance never marks anyone present — most likely participants are joining as PSTN dial-ins or with personal Microsoft accounts whose email doesn’t match. Have a student sign in with their work/school account that uses their EduBook email and re-test.