Microsoft Teams

SkillCommunication

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).

Use Microsoft Teams in Claude, ChatGPT or Ahel Desktop

Free. Sign in, add Microsoft Teams and connect your AI. About a minute.

Also: Claude Code · Cursor · Codex

Then ask your AI: use the Microsoft Teams skill

Details

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Microsoft TeamsStart free

What this skill tells your AI

The instructions your AI receives, as published by leoyeai/openclaw-master-skills in skills/microsoft-teams/SKILL.md and read by ahel’s review.

Access the Microsoft Teams API with managed OAuth authentication via Microsoft Graph. Manage teams, channels, messages, meetings, and access recordings and transcripts.

Quick Start

# 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

Base URL

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.

Authentication

All requests require the Maton API key in the Authorization header:

Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

export MATON_API_KEY="YOUR_API_KEY"

Getting Your API Key

  1. Sign in or create an account at maton.ai
  2. Go to maton.ai/settings
  3. Copy your API key

Connection Management

Manage your Microsoft Teams OAuth connections at https://ctrl.maton.ai.

List Connections

python <<'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

Create Connection

python <<'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

Get Connection

python <<'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:

{
  "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.

Delete Connection

python <<'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

Specifying Connection

If you have multiple Microsoft Teams connections, specify which one to use with the Maton-Connection header:

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"]}')
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.

API Reference

Teams

List Joined Teams
GET /microsoft-teams/v1.0/me/joinedTeams

Response:

{
  "@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"
    }
  ]
}
Get Team
GET /microsoft-teams/v1.0/teams/{team-id}

Channels

List Channels
GET /microsoft-teams/v1.0/teams/{team-id}/channels

Response:

{
  "@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
    }
  ]
}
List Private Channels
GET /microsoft-teams/v1.0/teams/{team-id}/channels?$filter=membershipType eq 'private'
Get Channel
GET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}
Create Channel
POST /microsoft-teams/v1.0/teams/{team-id}/channels
Content-Type: application/json

{
  "displayName": "New Channel",
  "description": "Channel description",
  "membershipType": "standard"
}

Response:

{
  "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
}
Update Channel
PATCH /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.

Delete Channel
DELETE /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}

Returns 204 No Content on success.

Channel Members

List Channel Members
GET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/members

Response:

{
  "@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"
    }
  ]
}

Messages

List Channel Messages
GET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages
Send Message to Channel
POST /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages
Content-Type: application/json

{
  "body": {
    "content": "Hello World"
  }
}

Response:

{
  "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"
  }
}
Send HTML Message
POST /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>"
  }
}
Reply to Message
POST /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies
Content-Type: application/json

{
  "body": {
    "content": "This is a reply"
  }
}
List Message Replies
GET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies
Edit Message
PATCH /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.

Team Members

List Team Members
GET /microsoft-teams/v1.0/teams/{team-id}/members

Response:

{
  "@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"
    }
  ]
}

Presence

Get User Presence
GET /microsoft-teams/v1.0/me/presence

Response:

{
  "id": "5f56d55b-2ffb-448d-982a-b52547431f71",
  "availability": "Offline",
  "activity": "Offline",
  "outOfOfficeSettings": {
    "message": null,
    "isOutOfOffice": false
  }
}

Availability values: Available, Busy, DoNotDisturb, Away, Offline

Get User Presence by ID
GET /microsoft-teams/v1.0/users/{user-id}/presence

Returns presence information for a specific user by their ID.

Tabs

List Channel Tabs
GET /microsoft-teams/v1.0/teams/{team-id}/channels/{channel-id}/tabs

Response:

{
  "@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/..."
    }
  ]
}

Apps

List Installed Apps
GET /microsoft-teams/v1.0/teams/{team-id}/installedApps

Online Meetings

Create Meeting
POST /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:

{
  "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.

Get Meeting
GET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}
Find Meeting by Join URL
GET /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.

List Calendar Events (includes scheduled meetings)
GET /microsoft-teams/v1.0/me/calendar/events?$top=10

Scheduled Teams meetings appear as calendar events with isOnlineMeeting: true.

Delete Meeting
DELETE /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}

Returns 204 No Content on success.

Create Meeting with Attendees
POST /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"
      }
    ]
  }
}
List Meeting Recordings
GET /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).

Get Meeting Recording
GET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/recordings/{recording-id}
List Meeting Transcripts
GET /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).

Get Meeting Transcript
GET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/transcripts/{transcript-id}
List Attendance Reports
GET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/attendanceReports

Returns attendance reports for a meeting (available after the meeting has ended).

Get Attendance Report
GET /microsoft-teams/v1.0/me/onlineMeetings/{meeting-id}/attendanceReports/{report-id}

Chats

List User Chats
GET /microsoft-teams/v1.0/me/chats
Get Chat
GET /microsoft-teams/v1.0/chats/{chat-id}
List Chat Messages
GET /microsoft-teams/v1.0/chats/{chat-id}/messages
Send Chat Message
POST /microsoft-teams/v1.0/chats/{chat-id}/messages
Content-Type: application/json

{
  "body": {
    "content": "Hello in chat"
  }
}

Pagination

Microsoft Graph uses OData-style pagination with @odata.nextLink:

GET /microsoft-teams/v1.0/me/joinedTeams?$top=10

Response includes pagination link when more results exist:

{
  "value": [...],
  "@odata.nextLink": "https://graph.microsoft.com/v1.0/me/joinedTeams?$skiptoken=..."
}

Use the $top parameter to limit results per page.

OData Query Parameters

  • $top=10 - Limit results
  • $skip=20 - Skip results
  • $select=id,displayName - Select specific fields
  • $filter=membershipType eq 'private' - Filter results
  • $orderby=displayName - Sort results

Code Examples

JavaScript

const 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();

Python

import 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()

Send Message Example (Python)

import 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()

Notes

  • Uses Microsoft Graph API v1.0
  • Messages are sent as the authenticated user (not as a bot) - the from.user field shows the actual user identity
  • Team IDs are GUIDs (e.g., b643f103-870d-4f98-a23d-e6f164fae33e)
  • Channel IDs include thread suffix (e.g., 19:9fwtZjo3IM0D8bLdQqR-_oMFw1eUDlzWjPfIhNGhVd41@thread.tacv2)
  • Message IDs are timestamps (e.g., 1771359569239)
  • Message body content types: text (default) or html
  • Channel membership types: standard, private, shared
  • The default "General" channel cannot be updated or deleted
  • Only me endpoint is supported for listing joined teams (not arbitrary user IDs)
  • IMPORTANT: When using curl commands, use curl -g when URLs contain brackets to disable glob parsing
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments

Error Handling

StatusMeaning
400Missing Microsoft Teams connection or invalid request
401Invalid or missing Maton API key
403Insufficient permissions for the requested resource
404Team, channel, or message not found
429Rate limited (Microsoft Graph throttling)
4xx/5xxPassthrough error from Microsoft Graph API

Resources

Signals

GitHub stars
2k
Forks
325
Last commit
Jul 2026

ahel review

  • S4info
    community integration, published by leoyeai, not microsoft
  • K2info
    exfiltration

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Item type
skill
Key
microsoft-teams-leoyeai
Source
github.com/leoyeai/openclaw-master-skills