Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Microsoft Teams API integration with managed OAuth. Manage teams, channels, messages, and meetings via Microsoft Graph API. Use this skill when users want to list teams, create channels, send messages, schedule meetings, or access meeting recordings and transcripts. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 317% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 302% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 321% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 404% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 166% | 0% |
Access the Microsoft Teams API with managed OAuth authentication via Microsoft Graph. Manage teams, channels, messages, meetings, and access recordings and transcripts.
bash# List user's joined teams python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/microsoft-teams/v1.0/me/joinedTeams') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
https://gateway.maton.ai/microsoft-teams/{native-api-path}Replace {native-api-path} with the actual Microsoft Graph API endpoint path. The gateway proxies requests to graph.microsoft.com and automatically injects your OAuth token.
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEYEnvironment Variable: Set your API key as MATON_API_KEY:
bashexport MATON_API_KEY="YOUR_API_KEY"
Manage your Microsoft Teams OAuth connections at https://ctrl.maton.ai.
bashpython <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections?app=microsoft-teams&status=ACTIVE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
bashpython <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'microsoft-teams'}).encode() req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
bashpython <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
Response:
json{ "connection": { "connection_id": "fb0fdc4a-0b5a-40cf-8b92-3bdae848cde3", "status": "ACTIVE", "creation_time": "2026-02-17T09:51:21.074601Z", "last_updated_time": "2026-02-17T09:51:34.323814Z", "url": "https://connect.maton.ai/?session_token=...", "app": "microsoft-teams", "metadata": {} } }
Open the returned url in a browser to complete OAuth authorization.
bashpython <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
If you have multiple Microsoft Teams connections, specify which one to use with the Maton-Connection header:
bashpython <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/microsoft-teams/v1.0/me/joinedTeams') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Maton-Connection', 'fb0fdc4a-0b5a-40cf-8b92-3bdae848cde3') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
If omitted, the gateway uses the default (oldest) active connection.
bashGET /microsoft-teams/v1.0/me/joinedTeams
Response:
json{ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams", "@odata.count": 1, "value": [ { "id": "b643f103-870d-4f98-a23d-e6f164fae33e", "displayName": "carvedai.com", "description": null, "isArchived": false, "tenantId": "cb83c3f9-6d16-4cf3-bd8c-ab16b37932f9" } ] }
bashGET /microsoft-teams/v1.0/teams/{team-id}
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels
Response:
json{ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams('...')/channels", "@odata.count": 1, "value": [ { "id": "19:9fwtZjo3IM0D8bLdQqR-_oMFw1eUDlzWjPfIhNGhVd41@thread.tacv2", "createdDateTime": "2026-02-16T20:09:27.254Z", "displayName": "General", "description": null, "email": "carvedai.com473@carvedai.com", "membershipType": "standard", "isArchived": false } ] }
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels?$filter=membershipType eq 'private'
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}
bashPOST /microsoft-teams/v1.0/teams/{team-id}/channels Content-Type: application/json { "displayName": "New Channel", "description": "Channel description", "membershipType": "standard" }
Response:
json{ "id": "19:3b3361df822044558a062bb1a4ac8357@thread.tacv2", "createdDateTime": "2026-02-17T20:24:33.9284462Z", "displayName": "Maton Test Channel", "description": "Channel created by Maton integration test", "membershipType": "standard", "isArchived": false }
bashPATCH /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id} Content-Type: application/json { "description": "Updated description" }
Returns 204 No Content on success. Note: The default "General" channel cannot be updated.
bashDELETE /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}
Returns 204 No Content on success.
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/members
Response:
json{ "@odata.count": 1, "value": [ { "@odata.type": "#microsoft.graph.aadUserConversationMember", "id": "MCMjMiMj...", "roles": ["owner"], "displayName": "Kevin Kim", "userId": "5f56d55b-2ffb-448d-982a-b52547431f71", "email": "richard@carvedai.com" } ] }
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages
bashPOST /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages Content-Type: application/json { "body": { "content": "Hello World" } }
Response:
json{ "id": "1771359569239", "replyToId": null, "messageType": "message", "createdDateTime": "2026-02-17T20:19:29.239Z", "importance": "normal", "locale": "en-us", "from": { "user": { "id": "5f56d55b-2ffb-448d-982a-b52547431f71", "displayName": "Kevin Kim", "userIdentityType": "aadUser", "tenantId": "cb83c3f9-6d16-4cf3-bd8c-ab16b37932f9" } }, "body": { "contentType": "text", "content": "Hello World" }, "channelIdentity": { "teamId": "b643f103-870d-4f98-a23d-e6f164fae33e", "channelId": "19:9fwtZjo3IM0D8bLdQqR-_oMFw1eUDlzWjPfIhNGhVd41@thread.tacv2" } }
bashPOST /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages Content-Type: application/json { "body": { "contentType": "html", "content": "<h1>Hello</h1><p>This is <strong>formatted</strong> content.</p>" } }
bashPOST /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies Content-Type: application/json { "body": { "content": "This is a reply" } }
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies
bashPATCH /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages/{message-id} Content-Type: application/json { "body": { "content": "Updated message content" } }
Returns 204 No Content on success.
bashGET /microsoft-teams/v1.0/teams/{team-id}/members
Response:
json{ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams('...')/members", "@odata.count": 1, "value": [ { "@odata.type": "#microsoft.graph.aadUserConversationMember", "id": "MCMjMSMj...", "roles": ["owner"], "displayName": "Kevin Kim", "userId": "5f56d55b-2ffb-448d-982a-b52547431f71", "email": "richard@carvedai.com", "tenantId": "cb83c3f9-6d16-4cf3-bd8c-ab16b37932f9" } ] }
bashGET /microsoft-teams/v1.0/me/presence
Response:
json{ "id": "5f56d55b-2ffb-448d-982a-b52547431f71", "availability": "Offline", "activity": "Offline", "outOfOfficeSettings": { "message": null, "isOutOfOffice": false } }
Availability values: Available, Busy, DoNotDisturb, Away, Offline
bashGET /microsoft-teams/v1.0/users/{user-id}/presence
Returns presence information for a specific user by their ID.
bashGET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/tabs
Response:
json{ "@odata.count": 2, "value": [ { "id": "ee0b3e8b-dfc8-4945-a45d-28ceaf787d92", "displayName": "Notes", "webUrl": "https://teams.microsoft.com/l/entity/..." }, { "id": "3ed5b337-c2c9-4d5d-b7b4-84ff09a8fc1c", "displayName": "Files", "webUrl": "https://teams.microsoft.com/l/entity/..." } ] }
bashGET /microsoft-teams/v1.0/teams/{team-id}/installedApps
bashPOST /microsoft-teams/v1.0/me/onlineMeetings Content-Type: application/json { "subject": "Team Sync", "startDateTime": "2026-02-18T10:00:00Z", "endDateTime": "2026-02-18T11:00:00Z" }
Response:
json{ "id": "MSo1ZjU2ZDU1Yi0yZmZi...", "subject": "Team Sync", "startDateTime": "2026-02-18T10:00:00Z", "endDateTime": "2026-02-18T11:00:00Z", "joinUrl": "https://teams.microsoft.com/l/meetup-join/...", "joinWebUrl": "https://teams.microsoft.com/l/meetup-join/...", "meetingCode": "28636743235745", "joinMeetingIdSettings": { "joinMeetingId": "28636743235745", "passcode": "qh37NK9V", "isPasscodeRequired": true }, "participants": { "organizer": { "upn": "richard@carvedai.com", "role": "presenter" } } }
The joinUrl can be shared with attendees to join the meeting.
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}
bashGET /microsoft-teams/v1.0/me/onlineMeetings?$filter=JoinWebUrl eq '{encoded-join-url}'
Note: Microsoft Graph requires a filter to query meetings. You cannot list all meetings without filtering by JoinWebUrl.
bashGET /microsoft-teams/v1.0/me/calendar/events?$top=10
Scheduled Teams meetings appear as calendar events with isOnlineMeeting: true.
bashDELETE /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}
Returns 204 No Content on success.
bashPOST /microsoft-teams/v1.0/me/onlineMeetings Content-Type: application/json { "subject": "Project Review", "startDateTime": "2026-02-18T14:00:00Z", "endDateTime": "2026-02-18T15:00:00Z", "participants": { "attendees": [ { "upn": "attendee@example.com", "role": "attendee" } ] } }
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/recordings
Returns a list of recordings for a meeting (available after the meeting has ended and recording was enabled).
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/recordings/{recording-id}
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/transcripts
Returns a list of transcripts for a meeting (available after the meeting has ended and transcription was enabled).
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/transcripts/{transcript-id}
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/attendanceReports
Returns attendance reports for a meeting (available after the meeting has ended).
bashGET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/attendanceReports/{report-id}
bashGET /microsoft-teams/v1.0/me/chats
bashGET /microsoft-teams/v1.0/chats/{chat-id}
bashGET /microsoft-teams/v1.0/chats/{chat-id}/messages
bashPOST /microsoft-teams/v1.0/chats/{chat-id}/messages Content-Type: application/json { "body": { "content": "Hello in chat" } }
Microsoft Graph uses OData-style pagination with @odata.nextLink:
bashGET /microsoft-teams/v1.0/me/joinedTeams?$top=10
Response includes pagination link when more results exist:
json{ "value": [...], "@odata.nextLink": "https://graph.microsoft.com/v1.0/me/joinedTeams?$skiptoken=..." }
Use the $top parameter to limit results per page.
$top=10 - Limit results$skip=20 - Skip results$select=id,displayName - Select specific fields$filter=membershipType eq 'private' - Filter results$orderby=displayName - Sort resultsjavascriptconst response = await fetch( 'https://gateway.maton.ai/microsoft-teams/v1.0/me/joinedTeams', { headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}` } } ); const data = await response.json();
pythonimport os import requests response = requests.get( 'https://gateway.maton.ai/microsoft-teams/v1.0/me/joinedTeams', headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'} ) data = response.json()
pythonimport os import requests team_id = "your-team-id" channel_id = "your-channel-id" response = requests.post( f'https://gateway.maton.ai/microsoft-teams/v1.0/teams/{team_id}/channels/{channel_id}/messages', headers={ 'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}', 'Content-Type': 'application/json' }, json={'body': {'content': 'Hello from Maton!'}} ) data = response.json()
from.user field shows the actual user identityb643f103-870d-4f98-a23d-e6f164fae33e)19:9fwtZjo3IM0D8bLdQqR-_oMFw1eUDlzWjPfIhNGhVd41@thread.tacv2)1771359569239)text (default) or htmlstandard, private, sharedme endpoint is supported for listing joined teams (not arbitrary user IDs)curl -g when URLs contain brackets to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| Status | Meaning | |--------|---------| | 400 | Missing Microsoft Teams connection or invalid request | | 401 | Invalid or missing Maton API key | | 403 | Insufficient permissions for the requested resource | | 404 | Team, channel, or message not found | | 429 | Rate limited (Microsoft Graph throttling) | | 4xx/5xx | Passthrough error from Microsoft Graph API |
Other measured skills in the registry, with their headline benchmark lift.