orbotodocs
Integrations

Calendar sync

See orboto schedules in your calendar - the personal feed, and two-way sync with Google, Microsoft 365, or any CalDAV server.

orboto offers two independent ways to connect a calendar: a read-only feed anyone can subscribe to in seconds, and a deeper provider sync that pushes your approved absences out and pulls your busy time in. You can use either on its own, or both together.

Calendar feed (any calendar app)

The feed is the fastest way to see orboto dates in a calendar you already use - no OAuth, no admin setup.

  1. Open your profile.
  2. Find Calendar feed and select Generate feed URL (or copy the existing one if you already generated it).
  3. Choose which schedule types the feed carries - due dates, milestones, absences.
  4. Copy the URL and add it as a subscription (not an import) in your calendar app - the exact menu differs per app, but it's the option labeled "Subscribe from URL", "Add calendar by URL", or similar, never "Import" (import is a one-time snapshot; subscribe keeps refreshing).

The profile page's calendar feed card with the generated URL and type checkboxes

The URL contains a private, unguessable token - anyone who has the URL can read the events it carries, so treat it like a password. If a URL leaks (pasted somewhere public, shared with the wrong person), select Regenerate - the old URL stops working immediately and every subscriber needs the new one.

The feed is read-only at the consuming end: your calendar app refreshes it periodically (the interval is controlled by the app, not orboto - most refresh every few hours) but you can't edit an orboto event from inside your calendar app and have it write back.

Provider sync (Google, Microsoft 365, CalDAV)

Provider sync is two-way in a specific sense:

  • Push - your approved absences become all-day events on a calendar you choose (your primary/default calendar unless you pick a different target).
  • Pull - your busy windows from that external calendar appear as a read-only hatched overlay on your Time Calendar, so you can plan orboto work against your real availability. orboto only ever reads busy/free status, never event titles or details - this is a deliberate privacy choice, not a current limitation.

Three providers are supported, each connected the same way from Profile → Connected calendars, but set up differently underneath.

Google Calendar

One-time workspace setup (operator). Before any user can connect Google Calendar, an operator registers one OAuth client in the Google Cloud Console that every user connects through:

  1. Open the Google Cloud Console and pick (or create) a project.

  2. APIs & Services → Library → enable the Google Calendar API.

  3. APIs & Services → OAuth consent screen → configure it (External or Internal - Internal is simplest if every orboto user is in the same Google Workspace organization).

  4. APIs & Services → Credentials → Create credentials → OAuth client ID → application type Web application.

  5. Under Authorized redirect URIs, add exactly:

    https://<your-orboto-host>/calendar/callback/google

    Use the exact host your workspace serves on. Add one entry per host if you run more than one (e.g. staging and production).

  6. Set the resulting Client ID and Client secret as environment variables on the orboto deployment:

    GOOGLE_CALENDAR_CLIENT_ID=...
    GOOGLE_CALENDAR_CLIENT_SECRET=...

Until both are set, Google Calendar simply doesn't appear in the "+ Connect" list on any user's profile - there's nothing to misconfigure partway.

Why two scopes. The connection requests https://www.googleapis.com/auth/calendar.events (to push - insert, update, delete the absence events orboto owns) and https://www.googleapis.com/auth/calendar.readonly (to list your calendars for the target picker, and to read free/busy for the overlay). The write scope alone can't enumerate calendars or query free/busy, so the read scope is added on top - it's additive, not a downgrade.

Per-user connect flow:

  1. Profile → Connected calendars → Connect Google Calendar.
  2. Your browser is sent to Google's consent screen.
  3. Approve access. Google redirects back to orboto.
  4. orboto stores the connection (encrypted) and runs an initial sync: your currently-approved absences get pushed, and the busy overlay fills in on the next scheduled pull.
  5. Back on your profile, pick a target calendar (defaults to your primary one) and toggle push / pull independently - you can run either direction alone if you only want one.

Keeping the connection alive. Google issues short-lived access tokens (about an hour); orboto refreshes them automatically ahead of expiry using the refresh token from your original consent - you never see this happen. If you revoke orboto's access from your Google account, or Google otherwise invalidates the grant, the next sync attempt fails, the connection is marked inactive, and you get a notification asking you to reconnect. Reconnecting just repeats the connect flow above.

If two edits collide. If you (or Google Calendar) also edit one of the events orboto pushed, the next sync overwrites your external edit with orboto's version - orboto is authoritative for what it pushed. This only applies to events orboto created; anything else on your calendar is untouched.

Microsoft 365 / Outlook

One-time workspace setup (operator). Register one Microsoft Entra ID (Azure AD) app that every user connects through:

  1. Open the Azure portalMicrosoft Entra ID → App registrations → New registration.

  2. Name it (e.g. "orboto calendar sync").

  3. Choose Supported account types - this decides single-tenant vs multi-tenant (see below):

    • Accounts in this organizational directory only → single-tenant.
    • Accounts in any organizational directory (optionally and personal Microsoft accounts) → multi-tenant.
  4. Under Redirect URI, pick platform Web and add exactly:

    https://<your-orboto-host>/calendar/callback/microsoft
  5. API permissions → Add a permission → Microsoft Graph → Delegated permissions → add Calendars.ReadWrite and offline_access. Grant admin consent if your tenant requires it.

  6. Certificates & secrets → New client secret → copy the secret value immediately (Azure only shows it once).

  7. Set these as environment variables on the orboto deployment:

    MICROSOFT_CALENDAR_CLIENT_ID=...
    MICROSOFT_CALENDAR_CLIENT_SECRET=...
    MICROSOFT_CALENDAR_TENANT=common

    Use common for multi-tenant (any work/school account across any Entra tenant can connect - the broadest reach, but a foreign tenant's admin may need to consent separately). Use your directory (tenant) id instead for single-tenant (only your own organization's accounts can connect - the tightest scope, and the right default when every orboto user is in the same Microsoft org).

Until the client id and secret are set, Microsoft 365 doesn't appear in the "+ Connect" list.

Why one scope covers both directions. Unlike Google, Calendars.ReadWrite alone covers push AND the reads needed for the calendar picker and the busy overlay - so Microsoft's scope set stays to just that plus offline_access (needed to get a refresh token, since access tokens only last about an hour).

Per-user connect flow:

  1. Profile → Connected calendars → Connect Microsoft 365.
  2. Consent on Microsoft's page.
  3. orboto stores the connection and runs an initial sync.
  4. Pick a target calendar (defaults to your default calendar) and toggle push / pull.

Keeping the connection alive. orboto refreshes access tokens proactively before they expire, and Microsoft rotates the refresh token on every refresh - orboto always re-stores the newest one, so this requires no attention from you. If Microsoft's API is temporarily rate-limiting requests, orboto waits and retries automatically; you'd only notice as a slightly delayed sync, never a failure. As with Google, if you revoke access or the grant otherwise becomes invalid, the next sync fails, the connection is marked inactive, and you're notified to reconnect.

If two edits collide, the same rule as Google applies: orboto is authoritative for events it pushed, and overwrites external edits to those specific events on the next sync.

CalDAV (self-hosted / other providers)

CalDAV is the option for any RFC-4791 server - Nextcloud, Mailcow, Fastmail, Posteo, mailbox.org, Apple iCloud, and more - and needs no workspace setup. It always appears under "+ Connect" because there's no OAuth app to register first; you connect directly with a server URL and credentials.

Per-user connect flow:

  1. Profile → Connected calendars → CalDAV.
  2. Fill in:
    • Server URL - the CalDAV entry point for your provider (see the per-provider values below). A quick-preset chip row prefills the common ones.
    • Username - usually your email address or account login.
    • Password - for any account with two-factor authentication (iCloud, Fastmail, many mailbox providers) this must be an app-specific password, not your normal login password; your regular password will be rejected by the provider itself.
  3. orboto verifies the credentials against the server before saving anything - a wrong URL or password fails immediately with a clear error, nothing partial gets stored. On success it lists your calendars and pre-selects the primary one as the sync target; change the target any time from the account card.

Per-provider server URL and password notes:

ProviderServer URLPassword
Nextcloud / ownCloudhttps://<your-host>/remote.php/davYour login password, or an app password from Security → Devices & sessions if 2FA is on.
Fastmailhttps://caldav.fastmail.comAn app password from Settings → Privacy & Security → App passwords, scoped to CalDAV - your normal password won't work.
Apple iCloudhttps://caldav.icloud.comAn app-specific password from account.apple.com → Sign-In & Security → App-Specific Passwords. iCloud rejects your plain Apple ID password over CalDAV.
Mailcow (SOGo)typically https://<host>/SOGo/davAn app password where the provider offers one.
Posteo, mailbox.org, and other generic RFC-4791 serversthe provider's published CalDAV endpoint (or your account's direct calendar URL if that's all you have - discovery degrades gracefully)An app password where the provider offers one.

Sync timing. Push is event-driven: approving, updating, or cancelling an absence sends the change to your calendar within moments. Pull runs on a schedule (about every 15 minutes) and covers the next 30 days of busy time; if nothing changed on your calendar since the last pull, orboto reuses the cached result instead of re-fetching, so an unchanged calendar doesn't cost extra requests.

If the server later rejects your stored credentials (password rotated, app password revoked), the next sync marks the account Reconnect and notifies you. Re-enter the credentials from the account card to resume - nothing else about the connection changes.

Self-hosted note. If your CalDAV server lives on a private network address (e.g. a LAN Nextcloud instance), the operator needs to set WEBHOOK_ALLOW_PRIVATE_IPS=true on the orboto deployment - by default, outbound requests to loopback/private/link-local addresses are blocked as an SSRF guard. This setting affects every outbound user-supplied URL on the instance (webhooks included), so only enable it when the orboto instance itself runs inside a trusted network.

Invitations

Scheduled items that involve other people (for example, planning sessions) go out as standard calendar invitations attached to the notification email - accepting one adds it to whatever calendar app you open the invitation with, independent of the feed and provider-sync paths above.

Troubleshooting

SymptomFix
The feed shows nothing / stopped updatingConfirm you subscribed (not imported) - an import is a one-time snapshot and will never update again. Also check the feed still has schedule types selected in your profile; an empty selection carries zero events by design.
Feed URL was pasted somewhere it shouldn't have beenRegenerate it from your profile immediately - the old URL stops working the instant you do, cutting off whoever has it, but every legitimate subscriber (including your own devices) needs to re-subscribe with the new one.
A provider doesn't appear in "+ Connect"For Google or Microsoft 365, this means the workspace-level OAuth app hasn't been registered yet - that's an operator task (see the setup steps above), not something a user can self-serve. CalDAV always appears; if it doesn't, something's wrong with the instance itself.
Connection shows "Reconnect"The stored credentials or token grant stopped working - most often you (or your IT) revoked access on the provider's side, a password rotated, or an app password was regenerated. Reconnect using the same steps as the initial connect.
Busy overlay is empty even though the connection is activePull runs on its own schedule (roughly every 15 minutes for CalDAV; Google/Microsoft similarly poll rather than push instantly) - give it a few minutes after connecting. If it's still empty after that, check the connected calendar actually has events in the next 30 days, and that pull is toggled on for that connection.
An event orboto pushed keeps reverting your manual editExpected behavior - orboto is authoritative for events it created and overwrites external edits to them on the next sync. Edit the absence in orboto instead of the calendar event directly.
CalDAV connect fails against a server on your local networkIf the CalDAV server is on a private/LAN address, ask your operator whether WEBHOOK_ALLOW_PRIVATE_IPS is enabled - by default the SSRF guard blocks private-network destinations.

On this page