usemultical.com · For Enterprise Reviewers
When you create or sign into your Multical account with Google, we request:
| Scope | Name | Why we need it |
|---|---|---|
openid | OpenID Connect | Verifies your identity |
email | Email address | Creates and links your account |
profile | Basic profile | Stores your display name |
When you connect a Google calendar for syncing, we request:
| Scope | Name | Why we need it |
|---|---|---|
googleapis.com/auth/calendar.events | Read & write calendar events | Read events to detect conflicts; create and delete blocking events |
googleapis.com/auth/userinfo.email | Email address | Identifies which Google account is being connected |
Multical also requests offline access so Google issues a refresh token, allowing continuous background syncing without requiring you to re-authenticate.
Multical registers a push notification channel with Google (via the Calendar API's watch
mechanism) so Google can notify Multical's server in real time when your calendar changes.
These channels expire after 7 days and are automatically renewed. Registering a channel
requires the same calendar.events scope already listed above. No additional
permissions are needed.
When you create or sign into your Multical account with Microsoft, we request:
| Scope | Name | Why we need it |
|---|---|---|
openid | OpenID Connect | Verifies your identity |
email | Email address | Creates and links your account |
profile | Basic profile | Stores your display name |
User.Read | Read user profile | Reads your name and email from Microsoft Graph |
When you connect a Microsoft calendar for syncing, we request:
| Scope | Name | Why we need it |
|---|---|---|
Calendars.ReadWrite | Read & write calendars | Read events to detect conflicts; create and delete blocking events |
User.Read | Read user profile | Identifies which Microsoft account is being connected |
offline_access | Offline / refresh token | Issues a refresh token for continuous background syncing |
openid | OpenID Connect | Required by Microsoft's OAuth 2.0 token endpoint |
Multical also registers a subscription with Microsoft Graph so Microsoft can notify
Multical's server in real time when your calendar changes. These subscriptions are
automatically renewed before expiry. Registering a subscription requires the
Calendars.ReadWrite scope already listed above. No additional permissions
are needed.
Apple Calendar does not use OAuth. Instead, Multical supports two connection methods depending on whether you need read-only or read-write access.
This method gives Multical full read-write access to your iCloud calendars, allowing it to both read your events and create blocking events directly in Apple Calendar.
| What you provide | Protocol | Why we need it |
|---|---|---|
| Apple ID email address | CalDAV over HTTPS | Identifies your iCloud account |
| App-specific password | CalDAV over HTTPS | Authenticates with iCloud without using your main Apple ID password |
App-specific passwords: Apple requires apps to use a dedicated app-specific password rather than your main Apple ID password. This is generated in your Apple ID account settings (appleid.apple.com) and can be revoked at any time without changing your main password. Multical never sees or stores your Apple ID password.
Credential storage: Your Apple ID email and app-specific password
are encrypted using AES-256-GCM before being stored, the same encryption used for
Google and Microsoft tokens. They are stored together with the CalDAV server URL
(caldav.icloud.com).
No webhooks: Apple's CalDAV does not support real-time push notifications. Multical syncs Apple calendars via a background job queue that polls every 15 to 30 minutes to check for new or changed events.
This method connects a calendar via a shareable iCal (.ics) URL.
It is read-only. Multical can read events from the calendar but cannot create blocking
events in it. You can use this as a source calendar in a sync rule.
| What you provide | Protocol | Why we need it |
|---|---|---|
iCal URL (webcal:// or https://) | iCal / ICS over HTTPS | Fetches calendar event data from the URL |
| Optional calendar label | — | Identifies the calendar in your Multical dashboard |
Read-only: Multical cannot create, edit, or delete events on an iCal URL calendar. Blocking events will only be created on other connected calendars, not this one.
URL storage: The iCal URL is encrypted using AES-256-GCM before being stored in the database, with a flag marking it as read-only.
SSRF protection: When fetching iCal URLs, Multical blocks requests to private, loopback, and internal network addresses to prevent server-side request forgery attacks. Fetches time out after 10 seconds.
CLASS:PRIVATE in the ICS data, signalling to calendar apps that the event is privateX-MULTICAL-BLOCK:TRUE property so Multical can identify and manage events it createdApple CalDAV calendars are polled on a schedule rather than receiving real-time updates. There may be up to a 15 to 30 minute delay before changes on your Apple Calendar are reflected in Multical.
Zoom integration is optional. You connect it if you want Multical to automatically create Zoom meetings when bookings are confirmed on a booking page configured to use Zoom as the video call type.
| Scope | Name | Why we need it |
|---|---|---|
meeting:write:meeting | Create meetings | Creates a Zoom meeting when a booking is confirmed |
meeting:update:meeting | Update meetings | Updates the meeting time when a booking is rescheduled |
meeting:delete:meeting | Delete meetings | Deletes the meeting when a booking is cancelled |
meeting:write:invite_links | Generate invite links | Generates participant join links |
user:read:user | Read user profile | Identifies your Zoom account |
user:read:email | Read email address | Stores which Zoom account is connected |
When a booking is confirmed on a page configured to use Zoom, Multical creates a Zoom meeting via the Zoom API with the following data:
[Booking page name]: [Attendee name], for example “30-min intro: Jane Smith”noneThe attendee's name is therefore shared with Zoom as part of the meeting topic. The attendee's email address is not sent to Zoom.
Multical explicitly sets auto_recording: 'none' on every Zoom meeting
it creates. Meetings created by Multical will not be automatically recorded.
Zoom OAuth tokens (access token and refresh token) are encrypted using AES-256-GCM before being stored, using the same algorithm and key as Google and Microsoft tokens. Tokens are automatically refreshed in the background. If a refresh fails, the stored credentials are deleted and you are prompted to reconnect.
When a booking is cancelled by you or the attendee, Multical deletes the associated Zoom meeting via the API. When a booking is rescheduled, Multical updates the existing Zoom meeting with the new time rather than creating a new one.
We access:
We do NOT access:
We do not store your calendar event details. Event content (titles, descriptions, locations, attendees, and video conference links) is fetched from your calendar provider at sync time, used to create blocking events, and immediately discarded. We store only what is needed to manage block lifecycle: event IDs, start/end times, and sync status.
Multical gives you direct control over how much information is shared when your calendars are synced. These are in-app settings you configure per sync rule.
For each sync rule, you choose one of three visibility modes:
| Mode | What colleagues see | How it protects you |
|---|---|---|
| Busy Only | Your custom block title only (e.g. “Busy”) | Hides all event details. No title, description, location, or video link from the source event is shared. |
| Title Only | Event title from the source calendar | Shares the meeting name but strips description, attendees, and optionally location and video links |
| Full Details | Full event title and description | Shares complete event details; location and video links are still individually toggleable |
Show Location: Off by default. When disabled, location details are stripped from blocking events on target calendars.
Show Video Link: Off by default. When disabled, video conferencing links (Google Meet, Teams, Zoom, etc.) are removed from blocking events. This prevents colleagues at one organisation seeing meeting links for another organisation's calls.
These settings are not available in Busy Only mode, which hides all details by design.
Include / Exclude filters: You can configure keyword filters per sync rule to control which events are synced at all. Events matching excluded keywords are never synced, keeping sensitive or personal events entirely off other calendars. Filters operate on event titles and descriptions.
Show Blocks As: You can override how blocking events appear on target calendars: Always Busy, Always Free, or match the source event.
Optional and unchecked by default: During signup, marketing communications consent is a separate optional checkbox, unchecked by default. This is independent of the required Terms of Service and Privacy Policy acceptance, in compliance with GDPR.
Multical allows you to create shareable booking pages so external parties can schedule time with you. This section explains what those external visitors can and cannot see, what data is collected from them, and how their data is protected.
When someone opens your booking link, the following information is returned from the server without requiring any authentication:
| Field | Visible to visitor | Notes |
|---|---|---|
| Booking page name | Yes | Set by you when creating the page |
| Description | Yes | Optional text you write |
| Meeting duration | Yes | e.g. 30 minutes |
| Available time slots | Yes | Computed from your calendar |
| Your timezone | Yes | Used to display slots correctly |
| Custom intake questions | Yes | Only questions you explicitly configure |
| Your logo | Yes | If you upload one |
| Your name or email | No | Never returned by the public API |
| Your calendar event titles or details | No | Never exposed to the visitor |
| What is blocking a given slot | No | Visitors see only available or unavailable, not why |
Availability calculation and calendar privacy: Available slots are computed by checking which times overlap with busy events on your connected calendars. The visitor is shown a list of open slots and is never told what event is occupying a blocked time, who it is with, or any other detail about your calendar contents.
When a visitor submits a booking, Multical collects and stores:
This data is stored in Multical's database and is visible to you (the booking page owner) in your dashboard. It is used to create a calendar event on your calendar and to send confirmation emails. It is not sold or shared with third parties.
Upon booking, the visitor receives a confirmation email containing:
.ics) file attachment so the visitor can add the event to their own calendarVisitors can cancel or reschedule their booking without creating a Multical account. This is managed via a secure cancellation token:
crypto.randomBytes(32))These endpoints are exempt from the session-based CSRF middleware because the visitor has no session. The token itself is the authentication mechanism.
You can configure a booking page to require your approval before a booking is confirmed. In this mode:
pending stateAvailability is re-checked at the moment a booking submission is received, not just when the page is loaded. If the slot becomes unavailable between page load and submission, the submission is rejected and the visitor is asked to choose another time.
Booking page endpoints do not require authentication but are still protected by:
The Portfolio View (also called the Big Picture view) is a unified calendar display that shows all of your real events across every connected calendar in a single view, automatically removing the duplicate “busy” blocking events that calendar sync creates. It is designed for fractionals, consultants, and multi-role professionals to see their full schedule in one place.
The Portfolio View is private and authenticated. All Portfolio View API endpoints require an active, authenticated session. The view is not publicly shareable and there is no public URL or share link that exposes your calendar events to anyone else.
Event content (titles, descriptions, attendees, links) is fetched from your calendar providers on demand and displayed in your browser. This content is not stored in Multical's database. It is retrieved in real time for display purposes only, consistent with Multical's approach across all features.
All of the following are implemented in the application code and verified against the codebase.
AES-256-GCM Encryption: All OAuth tokens (Google, Microsoft, Zoom)
and CalDAV credentials (Apple) are encrypted using AES-256-GCM before being stored
in the database. Each is encrypted with a unique random IV. The encryption key is
derived from a server environment variable using scrypt. This applies
equally to all providers.
Encrypted at rest: No credentials are stored as plain text. The encryption key material is held separately as a server environment variable.
Argon2 Hashing: User passwords are hashed using Argon2 with explicit parameters: memory cost 19,456 KiB, time cost 2 iterations, and parallelism 1. Plain-text passwords are never stored or logged.
HTTPS / TLS: All data is encrypted in transit. This includes
connections to Google, Microsoft, Apple's CalDAV server (caldav.icloud.com),
and Zoom.
Secure session cookies: Session cookies use HttpOnly,
Secure (production), and SameSite=lax flags, with a 24-hour
max-age. Sessions are stored server-side in Redis.
CSRF Protection: All mutating API requests from authenticated sessions
are protected by session-bound CSRF tokens. Tokens are generated using 32 bytes of
cryptographic randomness, verified via timing-safe comparison
(crypto.timingSafeEqual), and rotated on login and registration to prevent
token fixation. Unauthenticated public endpoints (booking submissions, cancel/reschedule
by token) are explicitly exempt from session CSRF checks and use other validation
mechanisms (token format validation, availability re-check, duration validation).
Separately, OAuth flows use a cryptographically random state parameter verified on
callback before any token exchange.
Rate Limiting — General API: 100 requests per 15 minutes per IP across all API endpoints, including public booking endpoints.
Rate Limiting — Auth Endpoints: 10 attempts per 15 minutes per
IP on login, registration, and password reset. Only failed attempts count
(skipSuccessfulRequests: true).
Rate Limiting — Calendar Operations: 20 requests per 15 minutes per IP on sync, event fetch, and data export endpoints.
Redis-backed sessions: Server-side sessions give full control to invalidate on logout immediately.
Async sync queue: Calendar sync jobs are processed through a Redis-backed Bull job queue with concurrency controls and automatic retries with exponential backoff, isolating sync failures from the main application.
HTTP Security Headers: Helmet.js sets CSP, X-Frame-Options
(DENY), X-Content-Type-Options, HSTS (2-year max-age with subdomains
and preload), Referrer-Policy (strict-origin-when-cross-origin), and
Permissions-Policy (camera=(), microphone=(), geolocation=()).
Applied as explicit fallbacks to survive Render/Cloudflare proxies.
Audit trail: Logins, OAuth connections, Apple CalDAV connections,
Zoom connections, calendar disconnections, and data export requests are all written
to an audit_log table with IP address and metadata.
Stripe (PCI DSS Level 1): Payment card data is handled entirely by Stripe. Multical never sees or stores card numbers. Stripe webhooks are verified using signature validation before processing.
Explicit consent at signup: Terms of Service and Privacy Policy acceptance is required. Timestamps stored in database.
Data export: Full JSON export (format version 1.1) of personal data, including account details, calendars, sync rules, events, bookings, and notification preferences, available at any time from account settings (GDPR Article 20). OAuth tokens are excluded from exports for security reasons.
Account deletion: Cascade deletion of all associated data on account deletion, including cancellation of active Stripe subscriptions and deregistration of provider webhooks. Session destroyed immediately.