microsoft-outlook

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Drive Microsoft Graph for Outlook / Microsoft 365 — both mail and calendar — via
curl + jq
. The user's OAuth bearer token is in
$MICROSOFT_OUTLOOK_TOKEN
; every call needs it as
Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN
. The token already carries the scopes the user agreed to at install: any of
Mail.Read
,
Mail.ReadWrite
,
Mail.Send
,
MailboxSettings.Read
,
MailboxSettings.ReadWrite
,
Calendars.Read
,
Calendars.ReadWrite
, plus
*.Shared
variants. Mail and calendar are unified into one connector (and one OAuth grant) because Microsoft Graph treats them as sibling features of the same mailbox — there is no value in splitting them at the skill layer.
The Graph API returns JSON; failures surface as
{"error": {"code": "...", "message": "..."}}
— show that error verbatim to the user.
Always start with
/me
to confirm the connection works AND learn which mailbox you're operating against. For calendar work, also fetch
mailboxSettings.timeZone
so dates render right.

通过
curl + jq
调用Microsoft Graph来操作Outlook/Microsoft 365的邮件日历功能。用户的OAuth承载令牌存储在
$MICROSOFT_OUTLOOK_TOKEN
中;每次调用都需要在请求头中添加
Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN
。该令牌已包含用户在安装时同意的权限范围:包括
Mail.Read
Mail.ReadWrite
Mail.Send
MailboxSettings.Read
MailboxSettings.ReadWrite
Calendars.Read
Calendars.ReadWrite
中的任意一项,以及
*.Shared
变体。邮件和日历功能被整合到一个连接器(及一个OAuth授权)中,因为Microsoft Graph将它们视为同一邮箱的同级功能——在技能层拆分它们没有意义。
Graph API返回JSON格式数据;请求失败时会返回
{"error": {"code": "...", "message": "..."}}
——需将该错误信息原封不动展示给用户。
始终以
/me
接口开始
,确认连接正常并了解当前操作的邮箱信息。处理日历相关操作时,还需获取
mailboxSettings.timeZone
以确保日期显示正确。

Mail — Recipes

邮件操作示例

Verify auth (always run first)

验证授权(必须首先执行)

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  https://graph.microsoft.com/v1.0/me \
  | jq '{displayName, mail, userPrincipalName}'
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  https://graph.microsoft.com/v1.0/me \
  | jq '{displayName, mail, userPrincipalName}'

List recent messages

列出最近收到的邮件

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages?\$top=10&\$select=id,subject,from,receivedDateTime,isRead,hasAttachments&\$orderby=receivedDateTime desc" \
  | jq '.value[] | {subject, from: .from.emailAddress.address, received: .receivedDateTime, unread: (.isRead | not)}'
Filters: append to URL with
&
(URL-encode the spaces).
WantAppend
Unread only
&$filter=isRead eq false
With attachments
&$filter=hasAttachments eq true
From a specific sender
&$filter=from/emailAddress/address eq 'user@example.com'
Date range
&$filter=receivedDateTime ge 2026-04-01T00:00:00Z and receivedDateTime lt 2026-05-01T00:00:00Z
CombineUse
and
/
or
between filter clauses
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages?\$top=10&\$select=id,subject,from,receivedDateTime,isRead,hasAttachments&\$orderby=receivedDateTime desc" \
  | jq '.value[] | {subject, from: .from.emailAddress.address, received: .receivedDateTime, unread: (.isRead | not)}'
筛选条件:通过
&
拼接在URL后(注意对空格进行URL编码)。
需求拼接参数
仅显示未读邮件
&$filter=isRead eq false
仅显示带附件的邮件
&$filter=hasAttachments eq true
来自特定发件人
&$filter=from/emailAddress/address eq 'user@example.com'
日期范围
&$filter=receivedDateTime ge 2026-04-01T00:00:00Z and receivedDateTime lt 2026-05-01T00:00:00Z
组合筛选在筛选条件间使用
and
/
or
连接

Search messages (full-text on subject + body)

搜索邮件(主题+正文全文检索)

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  --data-urlencode '$search="quarterly report"' \
  --data-urlencode '$top=10' \
  --data-urlencode '$select=id,subject,from,receivedDateTime' \
  --get https://graph.microsoft.com/v1.0/me/messages
$search
cannot be combined with
$filter
or
$orderby
in the same query — pick one.
$search
returns relevance-ranked results.
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  --data-urlencode '$search="quarterly report"' \
  --data-urlencode '$top=10' \
  --data-urlencode '$select=id,subject,from,receivedDateTime' \
  --get https://graph.microsoft.com/v1.0/me/messages
$search
无法与
$filter
$orderby
在同一查询中组合使用——需二选一。
$search
返回按相关性排序的结果。

Read a message body

读取邮件正文

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}?\$select=subject,body,from,toRecipients,receivedDateTime" \
  | jq '{subject, from: .from.emailAddress.address, received: .receivedDateTime, body: .body.content}'
body.contentType
is usually
"HTML"
. Use
jq -r .body.content
if you want the raw HTML.
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}?\$select=subject,body,from,toRecipients,receivedDateTime" \
  | jq '{subject, from: .from.emailAddress.address, received: .receivedDateTime, body: .body.content}'
body.contentType
通常为
"HTML"
。若需获取原始HTML内容,可使用
jq -r .body.content

Send an email

发送邮件

⚠️ ALWAYS use draft → confirm → send. NEVER call
/me/sendMail
directly — it sends immediately with no undo.
sh
undefined
⚠️ 必须遵循“创建草稿→确认内容→发送”流程。禁止直接调用
/me/sendMail
——该接口会立即发送邮件且无法撤销。
sh
undefined

Step 1: create draft

步骤1:创建草稿

DRAFT=$(curl -sS -X POST
-H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
-H "Content-Type: application/json"
-d "$(jq -nc
--arg subj "Project update"
--arg body "<p>Wanted to share the latest numbers.</p>"
--arg to "alice@example.com"
'{subject:$subj, body:{contentType:"HTML", content:$body}, toRecipients:[{emailAddress:{address:$to}}]}')"
https://graph.microsoft.com/v1.0/me/messages) DRAFT_ID=$(echo "$DRAFT" | jq -r .id)
DRAFT=$(curl -sS -X POST
-H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
-H "Content-Type: application/json"
-d "$(jq -nc
--arg subj "Project update"
--arg body "<p>Wanted to share the latest numbers.</p>"
--arg to "alice@example.com"
'{subject:$subj, body:{contentType:"HTML", content:$body}, toRecipients:[{emailAddress:{address:$to}}]}')"
https://graph.microsoft.com/v1.0/me/messages) DRAFT_ID=$(echo "$DRAFT" | jq -r .id)

Step 2: present the draft to the user — subject, recipients, body preview

步骤2:向用户展示草稿内容——主题、收件人、正文预览

echo "$DRAFT" | jq '{subject, to: .toRecipients[0].emailAddress.address, body: .body.content}'
echo "$DRAFT" | jq '{subject, to: .toRecipients[0].emailAddress.address, body: .body.content}'

Step 3: ONLY after user confirms — send (returns 202 No Content)

步骤3:仅在用户确认后发送(返回202 No Content)

curl -sS -X POST -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${DRAFT_ID}/send"
-w "HTTP %{http_code}\n"

CC / BCC: include `ccRecipients` / `bccRecipients` arrays in the same
shape as `toRecipients`.
curl -sS -X POST -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${DRAFT_ID}/send"
-w "HTTP %{http_code}\n"

抄送/密送:在请求体中添加与`toRecipients`格式相同的`ccRecipients` / `bccRecipients`数组即可。

Reply / reply-all / forward

回复/全部回复/转发

⚠️ Show the user your draft text + recipients before sending.
sh
undefined
⚠️ 发送前需向用户展示草稿内容及收件人信息。
sh
undefined

Quick reply (sends immediately on /reply — for explicit user-confirmed flow)

快速回复(调用/reply后立即发送——仅适用于用户明确确认的场景)

curl -sS -X POST
-H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
-H "Content-Type: application/json"
-d '{"comment":"Thanks for the update!"}'
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/reply"
curl -sS -X POST
-H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
-H "Content-Type: application/json"
-d '{"comment":"Thanks for the update!"}'
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/reply"

Or: createReply → review → /send (preferred for non-trivial replies)

或者:创建回复草稿→审核→发送(推荐用于非简单回复场景)

DRAFT=$(curl -sS -X POST -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/createReply") DRAFT_ID=$(echo "$DRAFT" | jq -r .id)
DRAFT=$(curl -sS -X POST -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/createReply") DRAFT_ID=$(echo "$DRAFT" | jq -r .id)

PATCH body if needed, then /send

如需修改正文可调用PATCH接口,之后再发送

Forward

转发邮件

curl -sS -X POST
-H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
-H "Content-Type: application/json"
-d "$(jq -nc --arg to "bob@example.com"
'{comment:"FYI", toRecipients:[{emailAddress:{address:$to}}]}')"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/forward"
undefined
curl -sS -X POST
-H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
-H "Content-Type: application/json"
-d "$(jq -nc --arg to "bob@example.com"
'{comment:"FYI", toRecipients:[{emailAddress:{address:$to}}]}')"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/forward"
undefined

Mark read / unread

标记已读/未读

sh
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"isRead": true}' \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}"
sh
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"isRead": true}' \
  "https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}"

List folders + read a specific folder

列出文件夹并读取指定文件夹内容

sh
undefined
sh
undefined

Well-known folder names: Inbox, Drafts, SentItems, DeletedItems, Archive, JunkEmail

常见文件夹名称:Inbox, Drafts, SentItems, DeletedItems, Archive, JunkEmail

curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/mailFolders('SentItems')/messages?\$top=5&\$select=subject,toRecipients,sentDateTime"
| jq '.value[] | {subject, sent: .sentDateTime}'
undefined
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/mailFolders('SentItems')/messages?\$top=5&\$select=subject,toRecipients,sentDateTime"
| jq '.value[] | {subject, sent: .sentDateTime}'
undefined

List + download attachments

列出并下载附件

sh
undefined
sh
undefined

Metadata

获取附件元数据

curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/attachments?\$select=id,name,size,contentType"
| jq '.value[] | {id, name, size}'
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/attachments?\$select=id,name,size,contentType"
| jq '.value[] | {id, name, size}'

Download a single attachment

下载单个附件

curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/attachments/${ATT_ID}/\$value"
-o "$SKILL_DIR/tmp/attachment.bin"
undefined
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}/attachments/${ATT_ID}/\$value"
-o "$SKILL_DIR/tmp/attachment.bin"
undefined

Mailbox settings (timezone, signature, automatic replies)

邮箱设置(时区、签名、自动回复)

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings"
Set an out-of-office reply:
⚠️ Confirm with user before changing — auto-reply will fire on every incoming mail.
sh
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"automaticRepliesSetting":{
        "status":"scheduled",
        "scheduledStartDateTime":{"dateTime":"2026-05-10T09:00:00","timeZone":"China Standard Time"},
        "scheduledEndDateTime":{"dateTime":"2026-05-15T18:00:00","timeZone":"China Standard Time"},
        "internalReplyMessage":"<p>I'm out this week, back Monday.</p>"}}' \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings"
Requires
MailboxSettings.ReadWrite
scope.
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings"
设置外出自动回复:
⚠️ 修改前需与用户确认——自动回复会对所有 incoming 邮件生效。
sh
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"automaticRepliesSetting":{
        "status":"scheduled",
        "scheduledStartDateTime":{"dateTime":"2026-05-10T09:00:00","timeZone":"China Standard Time"},
        "scheduledEndDateTime":{"dateTime":"2026-05-15T18:00:00","timeZone":"China Standard Time"},
        "internalReplyMessage":"<p>I'm out this week, back Monday.</p>"}}' \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings"
该操作需要
MailboxSettings.ReadWrite
权限。

Delete a message

删除邮件

⚠️ Always fetch the subject first and confirm with the user.
sh
undefined
⚠️ 必须先获取邮件主题并与用户确认后再执行删除操作。
sh
undefined

1) show what's about to be deleted

1) 展示即将删除的邮件信息

curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}?\$select=subject,from,receivedDateTime"
| jq '"Delete "(.subject)" from (.from.emailAddress.address) ((.receivedDateTime))?"'
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}?\$select=subject,from,receivedDateTime"
| jq '"Delete "(.subject)" from (.from.emailAddress.address) ((.receivedDateTime))?"'

2) after user confirms (moves to Deleted Items, returns 204)

2) 用户确认后执行删除(邮件会被移至已删除文件夹,返回204状态码)

curl -sS -X DELETE -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}"
-w "HTTP %{http_code}\n"

---
curl -sS -X DELETE -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN"
"https://graph.microsoft.com/v1.0/me/messages/${MSG_ID}"
-w "HTTP %{http_code}\n"

---

Calendar — Recipes

日历操作示例

Get user timezone (run once at start of any calendar work)

获取用户时区(处理任何日历操作前需执行一次)

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings" \
  | jq '.timeZone'
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/me/mailboxSettings" \
  | jq '.timeZone'

→ e.g. "China Standard Time"

→ 示例输出:"China Standard Time"


Pass that timezone in the `Prefer: outlook.timezone` header on every
calendar call so `start.dateTime` / `end.dateTime` come back rendered
in the user's local time:

```sh
TZ_HEADER='Prefer: outlook.timezone="China Standard Time"'

在每次日历调用中添加`Prefer: outlook.timezone`请求头,确保返回的`start.dateTime` / `end.dateTime`以用户本地时区显示:

```sh
TZ_HEADER='Prefer: outlook.timezone="China Standard Time"'

Today's agenda (calendarView)

今日日程(calendarView)

calendarView
expands recurring series into individual occurrences within the window — exactly what you want for an agenda. Plain
/events
returns only the recurrence master.
sh
START=$(date -u +'%Y-%m-%dT00:00:00Z')
END=$(date -u -v+1d +'%Y-%m-%dT00:00:00Z')   # macOS; use -d on Linux
curl -sS \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  --data-urlencode "startDateTime=$START" \
  --data-urlencode "endDateTime=$END" \
  --data-urlencode '$select=id,subject,start,end,location,attendees,onlineMeeting,isCancelled' \
  --data-urlencode '$orderby=start/dateTime' \
  --get https://graph.microsoft.com/v1.0/me/calendarView \
  | jq '.value[] | {subject, start: .start.dateTime, end: .end.dateTime, location: .location.displayName, attendees: [.attendees[].emailAddress.address]}'
calendarView
会将重复事件展开为指定时间范围内的单个实例——这正是日程展示所需的功能。而普通的
/events
接口仅返回重复事件的主条目。
sh
START=$(date -u +'%Y-%m-%dT00:00:00Z')
END=$(date -u -v+1d +'%Y-%m-%dT00:00:00Z')   # macOS系统;Linux系统使用-d参数
curl -sS \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  --data-urlencode "startDateTime=$START" \
  --data-urlencode "endDateTime=$END" \
  --data-urlencode '$select=id,subject,start,end,location,attendees,onlineMeeting,isCancelled' \
  --data-urlencode '$orderby=start/dateTime' \
  --get https://graph.microsoft.com/v1.0/me/calendarView \
  | jq '.value[] | {subject, start: .start.dateTime, end: .end.dateTime, location: .location.displayName, attendees: [.attendees[].emailAddress.address]}'

This week's events (Mon–Sun)

本周事件(周一至周日)

sh
START=$(date -u -v-Mon +'%Y-%m-%dT00:00:00Z' 2>/dev/null || date -u -d 'last monday' +'%Y-%m-%dT00:00:00Z')
END=$(date -u -v+7d -v-Mon +'%Y-%m-%dT00:00:00Z' 2>/dev/null || date -u -d 'next monday' +'%Y-%m-%dT00:00:00Z')
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  --data-urlencode "startDateTime=$START" \
  --data-urlencode "endDateTime=$END" \
  --data-urlencode '$select=subject,start,end' \
  --data-urlencode '$orderby=start/dateTime' \
  --get https://graph.microsoft.com/v1.0/me/calendarView
sh
START=$(date -u -v-Mon +'%Y-%m-%dT00:00:00Z' 2>/dev/null || date -u -d 'last monday' +'%Y-%m-%dT00:00:00Z')
END=$(date -u -v+7d -v-Mon +'%Y-%m-%dT00:00:00Z' 2>/dev/null || date -u -d 'next monday' +'%Y-%m-%dT00:00:00Z')
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  --data-urlencode "startDateTime=$START" \
  --data-urlencode "endDateTime=$END" \
  --data-urlencode '$select=subject,start,end' \
  --data-urlencode '$orderby=start/dateTime' \
  --get https://graph.microsoft.com/v1.0/me/calendarView

Find free / busy slots (
getSchedule
)

查找空闲/忙碌时段(
getSchedule

Best way to find a slot that works for multiple people. Returns 30-minute buckets of free / busy / tentative across the requested window.
sh
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc '{
        schedules: ["me", "alice@example.com", "bob@example.com"],
        startTime: {dateTime: "2026-05-05T09:00:00", timeZone: "China Standard Time"},
        endTime:   {dateTime: "2026-05-05T18:00:00", timeZone: "China Standard Time"},
        availabilityViewInterval: 30
      }')" \
  https://graph.microsoft.com/v1.0/me/calendar/getSchedule \
  | jq '.value[] | {who: .scheduleId, view: .availabilityView}'
这是查找多人共同空闲时段的最佳方式。返回指定时间范围内每30分钟的空闲/忙碌/暂定状态。
sh
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc '{
        schedules: ["me", "alice@example.com", "bob@example.com"],
        startTime: {dateTime: "2026-05-05T09:00:00", timeZone: "China Standard Time"},
        endTime:   {dateTime: "2026-05-05T18:00:00", timeZone: "China Standard Time"},
        availabilityViewInterval: 30
      }')" \
  https://graph.microsoft.com/v1.0/me/calendar/getSchedule \
  | jq '.value[] | {who: .scheduleId, view: .availabilityView}'

availabilityView is a string of digits: 0=free 1=tentative 2=busy 3=oof 4=workingElsewhere

availabilityView是一串数字:0=空闲 1=暂定 2=忙碌 3=外出 4=异地办公

undefined
undefined

Read a single event (incl. attendees + body)

读取单个事件(包含参会者及正文)

sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}?\$select=subject,start,end,location,attendees,body,organizer,onlineMeeting" \
  | jq '{subject, start: .start.dateTime, attendees: [.attendees[] | {addr: .emailAddress.address, response: .status.response}], body: .body.content}'
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Prefer: outlook.timezone=\"China Standard Time\"" \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}?\$select=subject,start,end,location,attendees,body,organizer,onlineMeeting" \
  | jq '{subject, start: .start.dateTime, attendees: [.attendees[] | {addr: .emailAddress.address, response: .status.response}], body: .body.content}'

Create an event

创建事件

⚠️ ALWAYS show subject / time / attendees to the user before creating — invitations fire automatically the moment the event is POSTed.
sh
PAYLOAD=$(jq -nc \
  --arg subj "Project sync" \
  --arg body "<p>Quarterly review.</p>" \
  --arg start "2026-05-06T10:00:00" \
  --arg end   "2026-05-06T10:30:00" \
  --arg tz    "China Standard Time" \
  --arg loc   "Meeting room 3F" \
  --arg a1    "alice@example.com" \
  '{
    subject: $subj,
    body:    {contentType:"HTML", content:$body},
    start:   {dateTime:$start, timeZone:$tz},
    end:     {dateTime:$end,   timeZone:$tz},
    location:{displayName:$loc},
    attendees:[{emailAddress:{address:$a1}, type:"required"}],
    isOnlineMeeting: true,
    onlineMeetingProvider: "teamsForBusiness"
  }')
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD" \
  https://graph.microsoft.com/v1.0/me/events \
  | jq '{id, subject, start: .start.dateTime, joinUrl: .onlineMeeting.joinUrl}'
isOnlineMeeting: true
+
onlineMeetingProvider: "teamsForBusiness"
auto-generates a Teams meeting link. Drop both for an in-person event.
⚠️ 创建前必须向用户展示主题、时间、参会者信息——事件创建完成后会立即向参会者发送邀请邮件。
sh
PAYLOAD=$(jq -nc \
  --arg subj "Project sync" \
  --arg body "<p>Quarterly review.</p>" \
  --arg start "2026-05-06T10:00:00" \
  --arg end   "2026-05-06T10:30:00" \
  --arg tz    "China Standard Time" \
  --arg loc   "Meeting room 3F" \
  --arg a1    "alice@example.com" \
  '{
    subject: $subj,
    body:    {contentType:"HTML", content:$body},
    start:   {dateTime:$start, timeZone:$tz},
    end:     {dateTime:$end,   timeZone:$tz},
    location:{displayName:$loc},
    attendees:[{emailAddress:{address:$a1}, type:"required"}],
    isOnlineMeeting: true,
    onlineMeetingProvider: "teamsForBusiness"
  }')
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$PAYLOAD" \
  https://graph.microsoft.com/v1.0/me/events \
  | jq '{id, subject, start: .start.dateTime, joinUrl: .onlineMeeting.joinUrl}'
设置
isOnlineMeeting: true
+
onlineMeetingProvider: "teamsForBusiness"
会自动生成Teams会议链接。若为线下会议,可移除这两个参数。

Update / reschedule (PATCH)

更新/重新安排事件(PATCH)

⚠️ Updating sends an "Updated" notice to all attendees. Confirm first.
sh
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc \
        --arg start "2026-05-06T14:00:00" \
        --arg end   "2026-05-06T14:30:00" \
        --arg tz    "China Standard Time" \
        '{start:{dateTime:$start, timeZone:$tz}, end:{dateTime:$end, timeZone:$tz}}')" \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}"
⚠️ 更新操作会向所有参会者发送“事件已更新”通知。执行前需与用户确认。
sh
curl -sS -X PATCH \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc \
        --arg start "2026-05-06T14:00:00" \
        --arg end   "2026-05-06T14:30:00" \
        --arg tz    "China Standard Time" \
        '{start:{dateTime:$start, timeZone:$tz}, end:{dateTime:$end, timeZone:$tz}}')" \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}"

Cancel a meeting (sends cancellation notice)

取消会议(发送取消通知)

⚠️ Confirm with the user — every attendee is notified.
sh
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"Need to reschedule, sorry."}' \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}/cancel" \
  -w "HTTP %{http_code}\n"
⚠️ 执行前需与用户确认——所有参会者都会收到通知。
sh
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"Need to reschedule, sorry."}' \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}/cancel" \
  -w "HTTP %{http_code}\n"

Accept / decline / tentative an incoming invite

接受/拒绝/暂定接受会议邀请

sh
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"See you there", "sendResponse":true}' \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}/accept"
sh
curl -sS -X POST \
  -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"comment":"See you there", "sendResponse":true}' \
  "https://graph.microsoft.com/v1.0/me/events/${EVENT_ID}/accept"

Or /decline, /tentativelyAccept

也可调用/decline(拒绝)或/tentativelyAccept(暂定接受)接口

undefined
undefined

Read a shared calendar

读取共享日历

Requires
Calendars.Read.Shared
.
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/users/${USER_UPN}/calendarView?startDateTime=${START}&endDateTime=${END}&\$select=subject,start,end" \
  -G
该操作需要
Calendars.Read.Shared
权限。
sh
curl -sS -H "Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN" \
  "https://graph.microsoft.com/v1.0/users/${USER_UPN}/calendarView?startDateTime=${START}&endDateTime=${END}&\$select=subject,start,end" \
  -G

Working with timezones

时区相关操作

FieldMeaning
start.dateTime
/
end.dateTime
The local wall-clock time.
start.timeZone
/
end.timeZone
IANA-ish name (
"Pacific Standard Time"
,
"China Standard Time"
,
"UTC"
).
Prefer: outlook.timezone="..."
request header
Re-renders all returned
dateTime
values into this zone.
Always set
Prefer: outlook.timezone
on read calls so the JSON arrives in the user's expected timezone instead of UTC.
字段含义
start.dateTime
/
end.dateTime
本地时钟时间。
start.timeZone
/
end.timeZone
IANA风格的时区名称(如
"Pacific Standard Time"
"China Standard Time"
"UTC"
)。
Prefer: outlook.timezone="..."
请求头
将返回的所有
dateTime
值转换为指定时区。
读取日历数据时务必设置
Prefer: outlook.timezone
请求头,确保返回的JSON数据使用用户预期的时区而非UTC。

Recurrence

重复事件

Use
calendarView
(it expands occurrences for you) — not
?$expand=
. To create a recurring event, include
recurrence
:
json
{
  "recurrence": {
    "pattern":  {"type":"weekly", "interval":1, "daysOfWeek":["monday","wednesday"]},
    "range":    {"type":"endDate", "startDate":"2026-05-06", "endDate":"2026-08-06"}
  }
}
To modify a single occurrence of a series, PATCH that occurrence's id (returned by
calendarView
), NOT the series master.

使用
calendarView
接口(它会自动展开重复事件实例)——不要使用
?$expand=
参数。创建重复事件时,需在请求体中包含
recurrence
字段:
json
{
  "recurrence": {
    "pattern":  {"type":"weekly", "interval":1, "daysOfWeek":["monday","wednesday"]},
    "range":    {"type":"endDate", "startDate":"2026-05-06", "endDate":"2026-08-06"}
  }
}
若需修改重复事件系列中的单个实例,需PATCH该实例的ID(由
calendarView
返回),而非重复事件的主条目ID。

OData quick reference (mail + calendar)

OData快速参考(邮件+日历)

ParamMail exampleCalendar example
$select
id,subject,from,receivedDateTime,isRead
subject,start,end,location,attendees
$filter
isRead eq false
start/dateTime ge '2026-05-01T00:00:00'
$orderby
receivedDateTime desc
start/dateTime
$top
10
browse,
25
search
10
browse
$search
"keyword"
(mail only — cannot combine with $filter / $orderby)
n/a
$expand
attachments
attendees
,
attachments
Use
--data-urlencode "$key=$value" --get
with curl to avoid shell-quoting
$
and spaces.
参数邮件示例日历示例
$select
id,subject,from,receivedDateTime,isRead
subject,start,end,location,attendees
$filter
isRead eq false
start/dateTime ge '2026-05-01T00:00:00'
$orderby
receivedDateTime desc
start/dateTime
$top
浏览时设为
10
,搜索时设为
25
浏览时设为
10
$search
"keyword"
(仅适用于邮件——无法与$filter/$orderby组合)
不适用
$expand
attachments
attendees
,
attachments
使用curl时,建议通过
--data-urlencode "$key=$value" --get
方式传递参数,避免shell对
$
和空格的转义问题。

Rules

规则

  • Always pass
    $select
    — defaults return 30+ fields per item.
  • $top=10
    for browse,
    25
    for search. Don't paginate past 50 unless asked.
  • HTML bodies only for mail.
    contentType: "Text"
    collapses whitespace.
  • Use
    calendarView
    for any agenda / "what's on my calendar" question.
    /events
    returns recurrence masters only.
  • Set
    Prefer: outlook.timezone
    on calendar read calls; otherwise
    dateTime
    comes back in UTC.
  • URL-encode message / event / attachment IDs if using them in a path — IDs can contain
    +
    ,
    /
    ,
    =
    . Use
    jq -sRr @uri
    .
  • Date math:
    date -u -v+1d
    works on macOS,
    date -u -d 'tomorrow'
    on Linux.
  • 始终传递
    $select
    参数
    ——默认返回每个条目包含30+个字段。
  • 浏览时设
    $top=10
    ,搜索时设
    $top=25
    。除非用户要求,否则分页不要超过50条。
  • 邮件正文仅使用HTML格式
    contentType: "Text"
    会导致空格被压缩。
  • 处理日程相关问题时使用
    calendarView
    接口
    /events
    仅返回重复事件的主条目。
  • 读取日历数据时设置
    Prefer: outlook.timezone
    请求头
    ;否则返回的
    dateTime
    值为UTC时区。
  • 若在路径中使用邮件/事件/附件ID,需进行URL编码——ID可能包含
    +
    /
    =
    字符。可使用
    jq -sRr @uri
    进行编码。
  • 日期计算:macOS系统使用
    date -u -v+1d
    ,Linux系统使用
    date -u -d 'tomorrow'

CRITICAL: User consent for destructive / notifying actions

重要提示:破坏性/通知类操作需用户确认

Sent emails cannot be unsent. Calendar writes fan out emails to attendees. Deleted messages may be permanently lost. Pattern: prepare → present → execute.
ActionPrepare stepShow user
Send mail
POST /me/messages
(draft)
subject, recipients, body preview
Reply / forwardcreateReply / createForwardquote snippet + your reply text
Delete mailfetch
subject
first
"Delete '{subject}' from {sender}?"
Out-of-officeshow current setting firstnew schedule + message preview
Create eventbuild payloadsubject, time, attendees, online-meeting on/off
Update eventdiff with currentwhat's changing, attendee count being notified
Cancel eventfetch event firstsubject, time, attendee count
Accept / decline invitefetch event firstevent subject + organizer
Bulklist affectedcount + sample
Never call
/me/sendMail
— it sends immediately with no undo. Always draft → confirm →
/send
.
已发送的邮件无法撤回。日历写入操作会向所有参会者发送邮件通知。删除的邮件可能永久丢失。 操作流程:准备→展示→执行
操作准备步骤展示给用户的内容
发送邮件调用
POST /me/messages
创建草稿
主题、收件人、正文预览
回复/转发调用createReply/createForward创建草稿引用片段+回复内容
删除邮件先获取邮件主题"是否删除来自{发件人}的'{主题}'邮件?"
设置外出自动回复先获取当前设置新的时间安排+回复内容预览
创建事件构建请求体主题、时间、参会者、是否开启线上会议
更新事件对比当前事件内容变更内容、将收到通知的参会者数量
取消事件先获取事件信息主题、时间、参会者数量
接受/拒绝邀请先获取事件信息事件主题+组织者
批量操作列出受影响的条目数量+示例条目
禁止调用
/me/sendMail
接口
——该接口会立即发送邮件且无法撤销。必须遵循“创建草稿→确认→调用/send”流程。

Errors

错误处理

  • 401 InvalidAuthenticationToken
    → token expired; user must reinstall the connector.
  • 403 ErrorAccessDenied
    → write scope missing (e.g. trying
    Mail.Send
    without it granted, or
    Calendars.ReadWrite
    for create / cancel); ask user to reinstall and tick the write scope.
  • 429 TooManyRequests
    → respect
    Retry-After
    header.
  • 404 ErrorItemNotFound
    → wrong message / event id (or it was already deleted / cancelled).
  • 401 InvalidAuthenticationToken
    → 令牌过期;用户需重新安装连接器。
  • 403 ErrorAccessDenied
    → 缺少写入权限(例如尝试调用
    Mail.Send
    但未获得该权限,或创建/取消事件时缺少
    Calendars.ReadWrite
    权限);请用户重新安装并勾选对应的写入权限。
  • 429 TooManyRequests
    → 需遵循
    Retry-After
    头指定的时间重试。
  • 404 ErrorItemNotFound
    → 邮件/事件ID错误(或已被删除/取消)。

Reference

参考链接