Functional description
Beheer > Integraties > Outlook

The Outlook integration synchronises appointments between a Microsoft 365 / Outlook calendar and i-Reserve. It talks directly to the Microsoft Graph REST API v1.0 over OAuth 2.0 — no EWS (Exchange Web Services) and no MSAL SDK are used. This article describes the functional background: which connection methods exist, how synchronisation works and what the limitations are.

Two connection methods

The method is selected with the authentication mode (config key auth_mode). Both methods can be served by the same Azure app; they are separate permission types in Entra ID.

 DelegatedApplication (app-only)
WhenOne personal or shared calendarMany room / resource mailboxes, at scale
AuthenticationInteractive OAuth (authorization code). A single user signs in and consents; i-Reserve stores an access and refresh token.Non-interactive (client credentials). The app authenticates as itself and fetches an app-only token (~1h TTL, no refresh token).
Calendar sourceThe connected user's default calendar (me/…).Per room mailbox (users/{room-upn}/…), each mapped to its own i-Reserve object.
DirectionBidirectional: inbound (Outlook → i-Reserve) and outbound (i-Reserve → Outlook).Inbound only. Outbound is disabled because an app-only token has no signed-in user (me).
Object mappingOne i-Reserve object.One object per room (room → object table). Scales to hundreds of rooms per customer.

Rule of thumb: use delegated for one calendar and one object. Use application as soon as you want to connect room / resource mailboxes — those cannot sign in interactively and have no licence, so delegated does not work for them.

How synchronisation works

Changes arrive near-real-time through a Graph change-notification subscription (webhook); a cron process catches missed notifications via polling. The integration deliberately uses both channels.

  • Inbound reads with a Graph delta query (calendarView/delta) over a −90 to +365 day window, pages through @odata.nextLink and persists the @odata.deltaLink as the sync token. In application mode each room has its own sync token and its own webhook.
  • Webhooks post to a public endpoint. A subscription expires after 3 days and is auto-renewed by cron once it is within 2 days of expiry. In application mode there is one subscription per room; notifications are routed by subscription id so exactly the right room is synced.
  • Polling fallback: if a calendar had no sync for more than 30 minutes, cron queues an inbound sync so missed notifications are recovered.

Inbound: from Outlook appointment to booking

For each Outlook event i-Reserve decides whether it is a create, update or cancellation:

  1. Match an existing booking — first on the stored external event id (field ires_field), falling back to a [booking_id] suffix in the subject.
  2. Removed / cancelled (@removed or isCancelled) → the linked booking gets the cancel status and is unlinked (unless it is in a lock status).
  3. Existing booking → date and time are updated, gated by “allow booking update” and “ignore validation errors”. Bookings created through this integration always follow the Outlook change.
  4. New → a booking is created on the mapped object (channel external/outlook), with subject and description as a remark. If the organiser matches a known customer, that customer is linked and (if configured) the “status with customer” is used.

Outbound (delegated only)

In delegated mode i-Reserve pushes appointments to Outlook based on booking status: create (POST me/events), update (PATCH me/events/{id}) and delete (DELETE me/events/{id}), driven by the “create calendar entry” and “delete on status” statuses. Optionally a Teams meeting link is created and the customer added as an attendee.

Limitations

  • Outbound is delegated-only. In application mode outbound triggers are deliberately skipped and logged.
  • Per-room state (sync token, webhook) is stored in the integration config; this scales to hundreds of rooms, but one integration = one Azure app/tenant.
  • An app-only token lives ~1h and is refreshed automatically; there is no refresh token.
  • There is no Graph room-list (places) auto-discovery: room UPNs are entered manually.