Telegraft

Integration

Microsoft 365 calendars, via Graph, without the timezone trap

Microsoft Graph lets a Telegram booking bot read availability and write appointments into Outlook calendars across a Microsoft 365 tenant. Admin consent is usually required, throttling is aggressive and undocumented in detail, and Graph's timezone handling causes more incorrect bookings than any other single factor.

Outlook Calendar integration: auth, limits and availability

Auth model
OAuth 2.0
Throttling
Per application and per mailbox; 429 with Retry-After
GCC availability
No restriction on Graph itself; tenant policy governs
Data flow
5 hops, worker-mediated

As of 2025-10-01, Telegram Bot API 13.4

Why this integration exists

Professional services firms, clinics attached to larger groups and anyone with a corporate IT function tend to be on Microsoft 365 rather than Google. The calendar is the same problem — staff already live in it, so a bot with its own diary creates a second truth — but the surrounding machinery is materially different and the differences are where projects lose time.

The first is consent. Google will often let an individual grant access to their own calendar; Microsoft tenants are usually configured so that application permissions require an administrator, and that administrator is an IT department with its own change process. This is a scheduling dependency in the project plan rather than a step a developer completes, and treating it otherwise is how a three-week build takes seven.

The second is time. Graph returns and accepts datetimes with an explicit timezone, and the tenant, the mailbox and the request can each carry a different one. A booking that is an hour out for half the year is the classic symptom, and it survives testing easily because everyone testing is in one timezone during one part of the year. Every datetime crossing this boundary carries its zone explicitly and is asserted in tests against a zone that observes daylight saving, because Gulf timezones do not and would hide the bug.

How the data actually moves

Customer inTelegramslot requestBot WorkergetScheduleMicrosoftGraphreserve slotD1 holdcreate eventMicrosoftGraph
Availability comes from getSchedule rather than by reading events, so the bot sees free and busy without reading the contents of anyone's calendar.

Graph uses OAuth 2.0, and for a business the right shape is application permissions with admin consent rather than delegated per-user access. Application permissions belong to the tenant and survive staff changes; delegated access belongs to whoever signed in and disappears with them. Tokens are cached in the Worker and refreshed before expiry.

Auth model: OAuth 2.0

Their limits, and what they mean for you

Graph throttles per application and per mailbox, returning 429 with a Retry-After header.

The Retry-After value is honoured rather than replaced with a local backoff. Microsoft publishes thresholds only loosely, so the header is the only reliable signal about how long to wait.

Change notification subscriptions expire within days and must be renewed.

Renewal runs well before expiry on a cron, and a failure raises an alert. A lapsed subscription means the bot stops seeing manual calendar edits, which degrades silently into wrong availability.

Datetimes carry an explicit timezone, and tenant, mailbox and request timezones can all differ.

Every datetime crossing the boundary is explicit about its zone. This is the single largest source of appointments booked an hour out, and it hides well in testing.

Application permissions for calendars require administrator consent in most tenant configurations.

A dependency on someone else's change process. It belongs in the plan as a scheduled item rather than as a task a developer ticks off.

How it fails, and what happens when it does

Bookings land an hour out for part of the year.

Almost always a timezone assumption. Tests assert against a zone that observes daylight saving, because Gulf timezones do not and would let the bug pass every local test.

A 429 is retried immediately and the application is throttled harder.

Retry-After is honoured. Ignoring it escalates the throttle, and a bot that retries aggressively can take itself out for considerably longer than the original limit.

A change notification subscription expires unnoticed.

Availability drifts from reality with no error anywhere. Renewal failures alert, because the failure mode is silent and slow rather than loud.

Admin consent is revoked during a tenant policy change.

All calendar operations fail at once. This is surfaced to staff immediately rather than retried, since no amount of retrying restores a withdrawn permission.

Availability in the UAE and the wider GCC

Global

No regional restriction on Graph itself. Tenant policy is set by your own IT function and is the real gate.

Microsoft 365 business tenants

The intended case. Application permissions with admin consent are the correct arrangement and survive staff turnover.

Personal Outlook accounts

Possible with delegated permissions and considerably more fragile, since access belongs to the individual rather than to an organisation.

Tenants with conditional access policies

Common in regulated GCC organisations and capable of blocking application access entirely. Confirm with IT before scoping rather than during the build.

When not to use this integration

  • Your IT function will not grant admin consent. Without it there is no viable integration, and this is worth establishing in the first conversation.
  • Conditional access policies block application permissions. Common in regulated organisations and a hard stop rather than a workaround.
  • Your staff live in Google Calendar despite the organisation being on Microsoft. Integrate with the calendar people actually use.
  • You need a scheduling product rather than a calendar writer. Graph moves events; it does not hold booking rules.

What it runs on

ComponentVersionWhy
Cloudflare WorkerscurrentGraph calls, token caching and subscription renewal on cron.
Cloudflare D1currentHolds, bookings and subscription expiry tracking.
Cloudflare KVcurrentAccess token cache shared across invocations.
Zod4.4Validation of Graph responses and change notifications.

Questions that come up during scoping

Why do bookings come out an hour wrong?

A timezone assumption, essentially every time. Graph carries an explicit zone on every datetime and the tenant, mailbox and request can each differ. It hides in testing because everyone tests in one zone during one part of the year.

Do we need our IT department involved?

Yes, for admin consent, and it is a scheduling dependency rather than a developer task. Starting that conversation before the build is commissioned routinely saves more time than any technical decision on the project.

How should throttling be handled?

By honouring the Retry-After header rather than applying a local backoff. Microsoft publishes thresholds only loosely, so the header is the one reliable signal — and retrying through a 429 escalates the throttle.

Does the bot read what is in people's calendars?

No. Availability uses getSchedule, which returns free and busy intervals without event content. That is both cheaper and much easier to justify to an IT function reviewing the permission request.

What happens if a change notification subscription lapses?

The bot stops seeing manual edits and availability drifts, with nothing erroring. Renewal runs well ahead of expiry and a renewal failure alerts, precisely because the failure mode is silent.

Can we support both Google and Microsoft calendars in one bot?

Yes, behind a common availability interface, and it is a normal requirement for a group with mixed practices. The provider-specific work is the timezone handling and the throttling behaviour, which do not generalise.

Related reading