Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/scheduling-oauth-apps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"coss": minor
"coss-cli": patch
---

Scheduling's own OAuth apps: `coss.scheduling.oauthApps` connects hosts' calendars through your own Google, Microsoft or Zoom app, so the consent screen shows your brand. Calendar connections gain `oauth_app`, settings gain `privacy_url`, and there are `scheduling.oauth_app.failing` and `.restored` webhooks. `coss open scheduling-oauth-apps` opens its dashboard page.
9 changes: 5 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ variables (`infra/test` enforces the list): a hash that differs between laptops
| `LOG_RETENTION_DAYS` | worker: days of environment logs kept (1-31, default 30); whole expired day partitions are dropped hourly |
| `SCHEDULING_DATA_KEYS` | api, web, worker: comma-separated 32-byte base64 keys, newest first, encrypting attendees' details and answers. Unset in production → Scheduling is off (503 `scheduling_unavailable`); a fixed development key otherwise |
| `SCHEDULING_PUBLIC_URL` | api, web, worker: origin of the hosted scheduling pages (`apps/scheduling`). Default `http://localhost:3005` outside production; unset in production → sessions have no `url` and emails no manage link |
| `GOOGLE_CALENDAR_CLIENT_ID`, `GOOGLE_CALENDAR_CLIENT_SECRET`, `MICROSOFT_CALENDAR_CLIENT_ID`, `MICROSOFT_CALENDAR_CLIENT_SECRET` | api, worker: Scheduling's OAuth apps for calendar access, separate from sign-in's. Redirect URIs `${API_PUBLIC_URL}/scheduling/oauth/{google,microsoft}/callback`. Each pair together, or that provider can't be connected |
| `ZOOM_CLIENT_ID`, `ZOOM_CLIENT_SECRET`, `ZOOM_WEBHOOK_SECRET_TOKEN` | api, worker: Scheduling's Zoom app, for meetings on hosts' accounts. Redirect URI `${API_PUBLIC_URL}/scheduling/oauth/zoom/callback`, deauthorization events at `${API_PUBLIC_URL}/scheduling/zoom/events`, verified with the token. The pair together, or Zoom can't be connected |
| `GOOGLE_CALENDAR_CLIENT_ID`, `GOOGLE_CALENDAR_CLIENT_SECRET`, `MICROSOFT_CALENDAR_CLIENT_ID`, `MICROSOFT_CALENDAR_CLIENT_SECRET` | api, worker: Scheduling's OAuth apps for calendar access, separate from sign-in's. Redirect URIs `${API_PUBLIC_URL}/scheduling/oauth/{google,microsoft}/callback`. Each pair together, or that provider can't be connected except through an environment's own OAuth app |
| `ZOOM_CLIENT_ID`, `ZOOM_CLIENT_SECRET`, `ZOOM_WEBHOOK_SECRET_TOKEN` | api, worker: Scheduling's Zoom app, for meetings on hosts' accounts. Redirect URI `${API_PUBLIC_URL}/scheduling/oauth/zoom/callback`, deauthorization events at `${API_PUBLIC_URL}/scheduling/zoom/events`, verified with the token. The pair together, or Zoom can't be connected except through an environment's own OAuth app |
| `SMS_PROVIDER` | api, web, worker: provider of live environments' SMS and of new numbers (`fake` default, or `twilio`). Production with `fake` refuses to buy numbers or submit registrations, so nothing live is sent; the api only needs the name (callbacks are verified with each subaccount's token). Shape in `@coss/sms/env` |
| `RISK_PHONE_LOOKUP`, `RISK_PHONE_SIM_SWAP` | api: risk's phone checks in live environments (`off` default: live checks answer 503; `twilio`: Twilio Lookup's line type and carrier with the parent account's `TWILIO_ACCOUNT_SID`/`TWILIO_AUTH_TOKEN`, paid per lookup — Twilio's DPA purpose must cover it first). `RISK_PHONE_SIM_SWAP=true` also asks for SIM swaps (only after Twilio's carrier approval and adding them back to the DPA: the phone check data and Annex 3's Twilio row). Sandbox environments use fixtures |
| `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_PRIMARY_CUSTOMER_PROFILE_SID`, `SMS_SUBACCOUNT_PREFIX` | web + worker with `SMS_PROVIDER=twilio` (the first two also api, with `RISK_PHONE_LOOKUP=twilio`): Cal.com's parent Twilio account (shared ISV), the approved primary customer profile organizations' 10DLC brands are registered under, and the prefix of the subaccounts coss.com creates (`<prefix>-<organization id>`). Twilio calls back `${API_PUBLIC_URL}/sms/twilio/*` |
Expand Down Expand Up @@ -534,7 +534,7 @@ notification emails) and, if there is useful advice, to `ADVICE` (`packages/repu
across orgs like `authorizeLiveSender`), `receiveEmail`/`routeRecipients` (receiving; from the envelope recipients), `recordSmsEvent`/`ingestSmsEvents` (from the `sms_id` of the callback URL), `receiveSms` (from the receiving number), `handleStatusCallback`/`handleInboundCallback` (from the callback's subaccount), `authorizeLiveSmsSender`, `ingestDmarcReports` (from the report address's token), `reencryptWebhookSecrets`, `reencryptSchedulingData` (operator scripts), `authenticateHookPortalSession` (from the session), `ingestSupportEmails` (one environment, across senders) and the rest of `@coss/support`'s worker functions, `submitForm`/`getHostedForm` (from the form id of `/f/:id`), `confirmFormSubmission`/`findConfirmation` (from the verification token), and the worker scans `runDueWebhookDeliveries`, `runDueLogDrainDeliveries`, `runDueHookDeliveries`, `parkHeldHookDeliveries`, `runDueHookRecoveries`, the Hooks purges, `recheckDueDomains`,
`recheckDueTrackingDomains`, `recheckDueReceivingDomains`, `reconcileDedicatedIpPools`, `sweepStaleClaims`, `sweepUnclaimedEmails`, `runDueReputationEvaluations`, `runDueSubaccountEvaluations`, `runDueOrganizationDeletions`, `runDueOrganizationLogos`, `runDueSubaccountDeletions`, `runDueDmarcNotifications`, `runDueMigrations`, `EmailService.dispatchDue`,
`unclaimedEmailBacklog`, `unclaimedSmsBacklog` (the `dispatch-backlog` loop: counts only), `SmsService.dispatchDue`, `sweepStaleSmsClaims`, `sweepUnclaimedSms`, `advanceDueSmsRegistrations`, `chargeDueSmsFees`, `chargeDueInboxFees`, `runDueReassignments`, `runDueHolidayCalendars`, `chargeDueHostFees`, `recordDueVerifySmsUsage` (bills each organization for its codes from coss.com's shared numbers, reading coss.com's messages),
`authenticateSchedulingSession` (from the token), `handleCalendarPush`, `completeCalendarAuthorization` (from the push's channel and the consent's state), `handleZoomDeauthorization` (from Zoom's signed event), `refreshIdleZoomConnections`, `runDueCalendarSyncs`, `runDueCalendarWrites`, `renewDueCalendarChannels`, `checkHostEditedEvents`, `runDueSchedulingNotifications`, `runDueCalendarDelegations`, `runDueReminderSyncs`, `expireDueBookings`, `announceDueOutOfOffice`,
`authenticateSchedulingSession` (from the token), `handleCalendarPush`, `completeCalendarAuthorization` (from the push's channel and the consent's state), `handleZoomDeauthorization` (from Zoom's signed event), `zoomOauthAppForEvents` (read only to check that signature), `warnOfExpiringOauthAppSecrets`, `refreshIdleZoomConnections`, `runDueCalendarSyncs`, `runDueCalendarWrites`, `renewDueCalendarChannels`, `checkHostEditedEvents`, `runDueSchedulingNotifications`, `runDueCalendarDelegations`, `runDueReminderSyncs`, `expireDueBookings`, `announceDueOutOfOffice`,
`runDueFormDeliveries`, `runDueFormStoreCleanup`
(and the dispatch jobs, from the job's `organizationId`); `@coss/sms/admin` is platform-level like `@coss/email/admin`. Server actions take only slugs +
target ids and derive the org from `requireOrgPermission`; every Better Auth organization call passes `organizationId`
Expand Down Expand Up @@ -800,7 +800,8 @@ notification emails) and, if there is useful advice, to `ADVICE` (`packages/repu

**Not built yet** (don't assume these exist; delete a line when it ships): OAuth for the hosted MCP server (it takes an API key in a header) and `COSS_MCP_TOOLSETS` there,
these parts of Support: on-call paging (so 24x7 can't be sold yet), a holiday calendar, automatic SLA credits, a support role narrower than platform admin, satisfaction ratings, AI drafts, presence, iOS screens, a public API, MCP and CLI tools,
these parts of Scheduling: iOS screens for out of office and reassignment, recurring out of office, out of office detected from a calendar, delegates for required hosts, Slack and HR apps, adding a host to a booking without replacing one, public booking pages, built-in payments, customers' own OAuth apps, routing forms, a workflow builder, waitlists, travel schedules, imports from Cal.com Platform, reminders at a local time of day, reminder copy per locale, quiet hours, a built-in rating page, fixed SMS numbers as reminder recipients, sending a test reminder, undoing a no-show, a per-attendee opt-out of reminder emails, reminders in the iOS app,
these parts of Scheduling: iOS screens for out of office and reassignment, recurring out of office, out of office detected from a calendar, delegates for required hosts, Slack and HR apps, adding a host to a booking without replacing one, public booking pages, built-in payments, routing forms, a workflow builder, waitlists, travel schedules, imports from Cal.com Platform, reminders at a local time of day, reminder copy per locale, quiet hours, a built-in rating page, fixed SMS numbers as reminder recipients, sending a test reminder, undoing a no-show, a per-attendee opt-out of reminder emails, reminders in the iOS app,
these parts of Scheduling's own OAuth apps: callback domains served by coss.com (a customer's own page forwards instead), more than one app per provider per environment, and managing them in the iOS app, MCP and CLI,
these parts of calendar delegation: automatic Microsoft secret rotation, coss.com-owned or keyless (`signJwt`) service accounts, certificate credentials, account-level Zoom, excluding one host while `match_host_email` is on, and delegation in the iOS app, MCP and CLI,
customers granting staff temporary access in HIPAA mode,
Verify's voice and WhatsApp channels, locales other than English, custom message templates,
Expand Down
Loading
Loading