Drive Microsoft Graph for Outlook / Microsoft 365 — both
mail and
calendar — via
. The user's OAuth bearer token is in
; every call needs it as
Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN
. The token already
carries the scopes the user agreed to at install: any of
,
,
,
,
MailboxSettings.ReadWrite
,
,
,
plus
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 to confirm the connection works AND learn
which mailbox you're operating against. For calendar work, also fetch
so dates render right.
通过
调用Microsoft Graph来操作Outlook/Microsoft 365的
邮件和
日历功能。用户的OAuth承载令牌存储在
中;每次调用都需要在请求头中添加
Authorization: Bearer $MICROSOFT_OUTLOOK_TOKEN
。该令牌已包含用户在安装时同意的权限范围:包括
、
、
、
、
MailboxSettings.ReadWrite
、
、
中的任意一项,以及
变体。邮件和日历功能被整合到一个连接器(及一个OAuth授权)中,因为Microsoft Graph将它们视为同一邮箱的同级功能——在技能层拆分它们没有意义。
Graph API返回JSON格式数据;请求失败时会返回
{"error": {"code": "...", "message": "..."}}
——需将该错误信息原封不动展示给用户。
始终以接口开始,确认连接正常并了解当前操作的邮箱信息。处理日历相关操作时,还需获取
以确保日期显示正确。
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).
| Want | Append |
|---|
| Unread only | |
| 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
|
| Combine | Use / 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=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
|
| 组合筛选 | 在筛选条件间使用 / 连接 |
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
cannot be combined with
or
in the same
query — pick one.
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
无法与
或
在同一查询中组合使用——需二选一。
返回按相关性排序的结果。
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}'
is usually
. Use
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}'
⚠️ ALWAYS use draft → confirm → send. NEVER call
directly — it sends immediately with no undo.
⚠️ 必须遵循“创建草稿→确认内容→发送”流程。禁止直接调用——该接口会立即发送邮件且无法撤销。
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)
Reply / reply-all / forward
回复/全部回复/转发
⚠️ Show the user your draft text + recipients before sending.
Quick reply (sends immediately on /reply — for explicit user-confirmed flow)
快速回复(调用/reply后立即发送——仅适用于用户明确确认的场景)
Or: createReply → review → /send (preferred for non-trivial replies)
或者:创建回复草稿→审核→发送(推荐用于非简单回复场景)
PATCH body if needed, then /send
如需修改正文可调用PATCH接口,之后再发送
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
列出文件夹并读取指定文件夹内容
Well-known folder names: Inbox, Drafts, SentItems, DeletedItems, Archive, JunkEmail
常见文件夹名称:Inbox, Drafts, SentItems, DeletedItems, Archive, JunkEmail
List + download attachments
列出并下载附件
Download a single attachment
下载单个附件
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
权限。
⚠️ Always fetch the subject first and confirm with the user.
⚠️ 必须先获取邮件主题并与用户确认后再执行删除操作。
1) show what's about to be deleted
1) 展示即将删除的邮件信息
2) after user confirms (moves to Deleted Items, returns 204)
2) 用户确认后执行删除(邮件会被移至已删除文件夹,返回204状态码)
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)
expands recurring series into individual occurrences
within the window — exactly what you want for an agenda. Plain
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]}'
会将重复事件展开为指定时间范围内的单个实例——这正是日程展示所需的功能。而普通的
接口仅返回重复事件的主条目。
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 ()
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=异地办公
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}'
⚠️ 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}'
+
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}'
设置
+
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(暂定接受)接口
Read a shared calendar
读取共享日历
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
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
时区相关操作
| Field | Meaning |
|---|
| / | The local wall-clock time. |
| / | IANA-ish name (, , ). |
Prefer: outlook.timezone="..."
request header | Re-renders all returned values into this zone. |
Always set
on read calls so the JSON arrives
in the user's expected timezone instead of UTC.
| 字段 | 含义 |
|---|
| / | 本地时钟时间。 |
| / | IANA风格的时区名称(如、、)。 |
Prefer: outlook.timezone="..."
请求头 | 将返回的所有值转换为指定时区。 |
读取日历数据时务必设置
请求头,确保返回的JSON数据使用用户预期的时区而非UTC。
Use
(it expands occurrences for you) — not
.
To create a recurring event, include
:
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
), NOT the series master.
使用
接口(它会自动展开重复事件实例)——不要使用
参数。创建重复事件时,需在请求体中包含
字段:
json
{
"recurrence": {
"pattern": {"type":"weekly", "interval":1, "daysOfWeek":["monday","wednesday"]},
"range": {"type":"endDate", "startDate":"2026-05-06", "endDate":"2026-08-06"}
}
}
若需修改重复事件系列中的单个实例,需PATCH该实例的ID(由
返回),而非重复事件的主条目ID。
OData quick reference (mail + calendar)
OData快速参考(邮件+日历)
| Param | Mail example | Calendar example |
|---|
| id,subject,from,receivedDateTime,isRead
| subject,start,end,location,attendees
|
| | start/dateTime ge '2026-05-01T00:00:00'
|
| | |
| browse, search | browse |
| (mail only — cannot combine with $filter / $orderby) | n/a |
| | , |
Use
--data-urlencode "$key=$value" --get
with curl to avoid
shell-quoting
and spaces.
| 参数 | 邮件示例 | 日历示例 |
|---|
| id,subject,from,receivedDateTime,isRead
| subject,start,end,location,attendees
|
| | start/dateTime ge '2026-05-01T00:00:00'
|
| | |
| 浏览时设为,搜索时设为 | 浏览时设为 |
| (仅适用于邮件——无法与$filter/$orderby组合) | 不适用 |
| | , |
使用curl时,建议通过
--data-urlencode "$key=$value" --get
方式传递参数,避免shell对
和空格的转义问题。
- Always pass — defaults return 30+ fields per item.
- for browse, for search. Don't paginate past 50 unless asked.
- HTML bodies only for mail. collapses whitespace.
- Use for any agenda / "what's on my calendar"
question. returns recurrence masters only.
- Set on calendar read calls; otherwise
comes back in UTC.
- URL-encode message / event / attachment IDs if using them in a
path — IDs can contain , , . Use .
- Date math: works on macOS, on Linux.
- 始终传递参数——默认返回每个条目包含30+个字段。
- 浏览时设,搜索时设。除非用户要求,否则分页不要超过50条。
- 邮件正文仅使用HTML格式。会导致空格被压缩。
- 处理日程相关问题时使用接口。仅返回重复事件的主条目。
- 读取日历数据时设置请求头;否则返回的值为UTC时区。
- 若在路径中使用邮件/事件/附件ID,需进行URL编码——ID可能包含、、字符。可使用进行编码。
- 日期计算:macOS系统使用,Linux系统使用。
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.
| Action | Prepare step | Show user |
|---|
| Send mail | (draft) | subject, recipients, body preview |
| Reply / forward | createReply / createForward | quote snippet + your reply text |
| Delete mail | fetch first | "Delete '{subject}' from {sender}?" |
| Out-of-office | show current setting first | new schedule + message preview |
| Create event | build payload | subject, time, attendees, online-meeting on/off |
| Update event | diff with current | what's changing, attendee count being notified |
| Cancel event | fetch event first | subject, time, attendee count |
| Accept / decline invite | fetch event first | event subject + organizer |
| Bulk | list affected | count + sample |
Never call — it sends immediately with no undo. Always draft → confirm →
.
已发送的邮件无法撤回。日历写入操作会向所有参会者发送邮件通知。删除的邮件可能永久丢失。
操作流程:准备→展示→执行。
| 操作 | 准备步骤 | 展示给用户的内容 |
|---|
| 发送邮件 | 调用创建草稿 | 主题、收件人、正文预览 |
| 回复/转发 | 调用createReply/createForward创建草稿 | 引用片段+回复内容 |
| 删除邮件 | 先获取邮件主题 | "是否删除来自{发件人}的'{主题}'邮件?" |
| 设置外出自动回复 | 先获取当前设置 | 新的时间安排+回复内容预览 |
| 创建事件 | 构建请求体 | 主题、时间、参会者、是否开启线上会议 |
| 更新事件 | 对比当前事件内容 | 变更内容、将收到通知的参会者数量 |
| 取消事件 | 先获取事件信息 | 主题、时间、参会者数量 |
| 接受/拒绝邀请 | 先获取事件信息 | 事件主题+组织者 |
| 批量操作 | 列出受影响的条目 | 数量+示例条目 |
禁止调用接口——该接口会立即发送邮件且无法撤销。必须遵循“创建草稿→确认→调用/send”流程。
401 InvalidAuthenticationToken
→ token expired; user must reinstall the connector.
- → write scope missing (e.g. trying without it granted, or for create / cancel); ask user to reinstall and tick the write scope.
- → respect header.
- → wrong message / event id (or it was already deleted / cancelled).
401 InvalidAuthenticationToken
→ 令牌过期;用户需重新安装连接器。
- → 缺少写入权限(例如尝试调用但未获得该权限,或创建/取消事件时缺少权限);请用户重新安装并勾选对应的写入权限。
- → 需遵循头指定的时间重试。
- → 邮件/事件ID错误(或已被删除/取消)。