Microsoft Outlook Calendar Tool
Read Outlook calendars and what is actually scheduled, create events, and find meeting times. For availability checks, room and person booking audits, and booking a meeting from a job.
Tool ID
msgraph-calendar
Credential Required
Yes — the shared msgraph credential, owned by the msgraph connector. Configure the tenant
once and all nine tools use it; rotating the client secret is one edit rather than nine.
It is a server-managed OAuth credential: InTouch mints and refreshes the access token itself and
the connector never sees the secret. For unattended work set grant_type to client_credentials
(the app-only grant) — an app-only token belongs to the application, so a 06:00 schedule does not
run as a person, see only what that person can see, and stop working the day they leave.
Credential Properties
| Property | Type | Default | Description |
|---|---|---|---|
client_id |
string | — | Required. The Entra app registration's Application (client) ID. |
client_secret |
string | — | Required for app-only. The secret's Value, not its Secret ID. |
token_url |
string | — | Required. https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token |
grant_type |
string | (blank) | client_credentials for unattended app-only use. Blank means the user-delegated refresh-token flow. |
scope |
string | — | https://graph.microsoft.com/.default — resolves to whatever application permissions an admin has consented to. |
access_token |
string | — | Machine-managed. Leave empty; the server mints and persists it. |
refresh_token |
string | — | Delegated credentials only. Not used by the app-only grant. |
expires_at |
string | 0 |
Machine-managed expiry, epoch seconds. |
Entra setup
- https://entra.microsoft.com → App registrations → your app → API permissions.
- Add a permission → Microsoft Graph → Application permissions — not Delegated. Picking Delegated is the most common failure: it consents cleanly and then 403s at runtime.
- Tick the permissions listed under Permissions below, then Grant admin consent. The Status column must read Granted.
- Permissions live in the token, not the portal. A token minted before the grant carries the old
claims for about an hour — blank
access_tokento force a fresh mint.
Permissions
Calendars.Read, or Calendars.ReadWrite to create events.
Operations
1. list_calendars — List calendars
The calendars on a mailbox. Calls GET /users/{id}/calendars.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. list_calendars |
id |
string | — | Required. The MAILBOX whose calendar this is: a user id or userPrincipalName (e.g. [email protected]). Required by every operation — there is no 'me' on an app-only credential. |
query |
string | — | Optional. OData query params as a JSON object string. REQUIRED for list_calendar_view, which needs its window: {"startDateTime": "2026-09-01T00:00:00Z", "endDateTime": "2026-09-08T00:00:00Z", "$select": "subject,start,end,organizer", "$orderby": "start/dateTime"}. Without both bounds Graph returns 400. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
2. list_events — List stored events
Stored event objects — series masters, not occurrences. See the notes. Calls GET /users/{id}/events.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. list_events |
id |
string | — | Required. The MAILBOX whose calendar this is: a user id or userPrincipalName (e.g. [email protected]). Required by every operation — there is no 'me' on an app-only credential. |
query |
string | — | Optional. OData query params as a JSON object string. REQUIRED for list_calendar_view, which needs its window: {"startDateTime": "2026-09-01T00:00:00Z", "endDateTime": "2026-09-08T00:00:00Z", "$select": "subject,start,end,organizer", "$orderby": "start/dateTime"}. Without both bounds Graph returns 400. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
3. get_event — Get one event
One event by id. Calls GET /users/{id}/events/{eventId}.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. get_event |
id |
string | — | Required. The MAILBOX whose calendar this is: a user id or userPrincipalName (e.g. [email protected]). Required by every operation — there is no 'me' on an app-only credential. |
eventId |
string | — | Required. Event id. Required by get_event. |
query |
string | — | Optional. OData query params as a JSON object string. REQUIRED for list_calendar_view, which needs its window: {"startDateTime": "2026-09-01T00:00:00Z", "endDateTime": "2026-09-08T00:00:00Z", "$select": "subject,start,end,organizer", "$orderby": "start/dateTime"}. Without both bounds Graph returns 400. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
4. list_calendar_view — List occurrences in a window
What is actually scheduled between two instants, with recurrences expanded. Requires startDateTime and endDateTime in query. Calls GET /users/{id}/calendarView.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. list_calendar_view |
id |
string | — | Required. The MAILBOX whose calendar this is: a user id or userPrincipalName (e.g. [email protected]). Required by every operation — there is no 'me' on an app-only credential. |
query |
string | — | Optional. OData query params as a JSON object string. REQUIRED for list_calendar_view, which needs its window: {"startDateTime": "2026-09-01T00:00:00Z", "endDateTime": "2026-09-08T00:00:00Z", "$select": "subject,start,end,organizer", "$orderby": "start/dateTime"}. Without both bounds Graph returns 400. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
5. create_event — Create an event
Book a meeting. Calls POST /users/{id}/events.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. create_event |
id |
string | — | Required. The MAILBOX whose calendar this is: a user id or userPrincipalName (e.g. [email protected]). Required by every operation — there is no 'me' on an app-only credential. |
body |
string | — | Required. JSON object string. create_event: {"subject": "Review", "start": {"dateTime": "2026-09-01T14:00:00", "timeZone": "Europe/London"}, "end": {"dateTime": "2026-09-01T15:00:00", "timeZone": "Europe/London"}, "attendees": [{"emailAddress": {"address": "[email protected]"}, "type": "required"}]}. find_meeting_times: {"attendees": [...], "meetingDuration": "PT1H"}. |
query |
string | — | Optional. OData query params as a JSON object string. REQUIRED for list_calendar_view, which needs its window: {"startDateTime": "2026-09-01T00:00:00Z", "endDateTime": "2026-09-08T00:00:00Z", "$select": "subject,start,end,organizer", "$orderby": "start/dateTime"}. Without both bounds Graph returns 400. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
6. find_meeting_times — Find meeting times
Candidate slots for a set of attendees and a duration. Calls POST /users/{id}/findMeetingTimes.
Properties:
| Property | Type | Default | Description |
|---|---|---|---|
operation |
string | — | Required. find_meeting_times |
id |
string | — | Required. The MAILBOX whose calendar this is: a user id or userPrincipalName (e.g. [email protected]). Required by every operation — there is no 'me' on an app-only credential. |
body |
string | — | Required. JSON object string. create_event: {"subject": "Review", "start": {"dateTime": "2026-09-01T14:00:00", "timeZone": "Europe/London"}, "end": {"dateTime": "2026-09-01T15:00:00", "timeZone": "Europe/London"}, "attendees": [{"emailAddress": {"address": "[email protected]"}, "type": "required"}]}. find_meeting_times: {"attendees": [...], "meetingDuration": "PT1H"}. |
query |
string | — | Optional. OData query params as a JSON object string. REQUIRED for list_calendar_view, which needs its window: {"startDateTime": "2026-09-01T00:00:00Z", "endDateTime": "2026-09-08T00:00:00Z", "$select": "subject,start,end,organizer", "$orderby": "start/dateTime"}. Without both bounds Graph returns 400. |
Published Outputs:
- result — the JSON response body
- status — HTTP status code
Paging
Graph answers a collection with 100 rows and an @odata.nextLink, so a naive listing returns a
fraction of a large tenant with HTTP 200 and no sign anything is missing. This connector follows
the continuation link until the collection is exhausted. If it stops at the page cap it says so —
the result carries complete: false with a warning, and the step returns WARNING rather than
SUCCESS. A page cap must never look like the end of the data.
Notes
list_events and list_calendar_view are not the same question, and the wrong one looks right.
list_events returns stored event objects: a weekly stand-up is one row, the series master, with
a recurrence rule attached and a start date that may be years ago. list_calendar_view expands
that series into the individual occurrences inside a window. "What is on this calendar next week"
is a list_calendar_view question every time; ask it of list_events and you get a series master
dated 2019 and no occurrences at all, with HTTP 200 beside it.
list_calendar_view requires its window as startDateTime and endDateTime in query.
Without them Graph returns 400 — correctly, since an unbounded expansion of every recurring meeting
a person ever accepted has no end.
The scope is the whole tenant until someone narrows it. Application Calendars.Read covers
every mailbox's calendar. The same Exchange application access policy that restricts mail restricts
this too — see the msgraph-mail README.
Times come back in UTC unless the event carries a timeZone. A report that prints the raw
value and calls it local time will be an hour wrong for half the year.
Self-Contained JAR
The jar bundles all runtime dependencies; only intouch-tool-api is provided by the server.
Publishes
The keys a later task may reference as {{taskName.key}}. Referencing anything not listed here resolves to nothing at run time.
completeoperationresultstatuswarning