* feat: calendar cache and sync - wip
* Add env.example
* refactor on CalendarCacheEventService
* remove test console.log
* Fix type checks errors
* chore: remove pt comment
* add route.ts
* chore: fix tests
* Improve cache impl
* chore: update recurring event id
* chore: small improvements
* calendar cache improvements
* Fix remove dynamic imports
* Add cleanup stale cache
* Fix tests
* add event update
* type fixes
* feat: add comprehensive tests for new calendar subscription API routes
- Add tests for /api/cron/calendar-subscriptions-cleanup route (9 tests)
- Add tests for /api/cron/calendar-subscriptions route (10 tests)
- Add tests for /api/webhooks/calendar-subscription/[provider] route (11 tests)
- Add missing feature flags for calendar-subscription-cache and calendar-subscription-sync
- All 30 tests pass with comprehensive coverage of authentication, feature flags, error handling, and service instantiation
Tests cover:
- Authentication scenarios (API key validation, Bearer tokens, query parameters)
- Feature flag combinations (cache/sync enabled/disabled states)
- Success and error handling (including non-Error exceptions)
- Service instantiation with proper dependency injection
- Provider validation for webhook endpoints
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add comprehensive tests for calendar subscription services, repositories, and adapters
- Add unit tests for CalendarSubscriptionService with subscription, webhook, and event processing
- Add unit tests for CalendarCacheEventService with cache operations and cleanup
- Add unit tests for CalendarSyncService with Cal.com event filtering and booking operations
- Add unit tests for CalendarCacheEventRepository with CRUD operations
- Add unit tests for SelectedCalendarRepository with calendar selection management
- Add unit tests for GoogleCalendarSubscriptionAdapter with subscription and event fetching
- Add unit tests for Office365CalendarSubscriptionAdapter with placeholder implementation
- Add unit tests for AdaptersFactory with provider management and adapter creation
- Fix lint issues by removing explicit 'any' type casting and unused variables
- All tests follow Cal.com conventions using Vitest framework with proper mocking
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: improve calendar-subscriptions-cleanup test performance by adding missing mocks
- Add comprehensive mocks for defaultResponderForAppDir, logger, performance monitoring, and Sentry
- Fix slow test execution (933ms -> <100ms) caused by missing dependency mocks
- Ensure consistent test performance across different environments
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* Fix tests
* Fix tests
* type fix
* Fix coderabbit comments
* Fix types
* Fix test
* Update apps/web/app/api/cron/calendar-subscriptions/route.ts
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Fixes by first review
* feat: add database migrations for calendar cache and sync fields
- Add CalendarCacheEventStatus enum with confirmed, tentative, cancelled values
- Add new fields to SelectedCalendar: channelId, channelKind, channelResourceId, channelResourceUri, channelExpiration, syncSubscribedAt, syncToken, syncedAt, syncErrorAt, syncErrorCount
- Create CalendarCacheEvent table with foreign key to SelectedCalendar
- Add necessary indexes and constraints for performance and data integrity
Fixes database schema issues causing e2e test failures with 'column does not exist' errors.
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* only google-calendar for now
* docs: add Calendar Cache and Sync feature documentation
- Add comprehensive feature overview and motivation
- Document feature flags with SQL examples
- Include SQL examples for enabling features for users and teams
- Reference technical documentation files
Addresses PR #23876 documentation requirements
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* docs: update calendar subscription README with comprehensive documentation
- Undo incorrect changes to main README.md
- Update packages/features/calendar-subscription/README.md with:
- Feature overview and motivation
- Environment variables section
- Complete feature flags documentation with SQL examples
- SQL examples for enabling features for users and teams
- Detailed architecture documentation
Addresses PR #23876 documentation requirements
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix docs
* Fix test to available calendars
* Fix test to available calendars
* add migration and sync boilerplate
* fix typo
* remove double log
* sync boilerplate
* remove console.log
* only subscribe for google calendar
* adjust for 3 months fetch
* only subscribe for teams that have feature enabled
* adjust tests
* chore: safe increment error count
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* calendar sync
* test: add comprehensive tests for CalendarSyncService
- Add comprehensive test coverage for CalendarSyncService methods
- Test handleEvents, cancelBooking, and rescheduleBooking functionality
- Cover edge cases like missing UIDs, malformed UIDs, and error handling
- Fix dynamic import usage in CalendarSyncService to use .default
- Remove invalid properties from handleNewBooking call to fix type errors
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: add skipCalendarSyncTaskCreation flag to handleCancelBooking to prevent infinite loops
- Add skipCalendarSyncTaskCreation field to bookingCancelSchema in zod-utils.ts
- Update handleCancelBooking to skip EventManager.cancelEvent when flag is true
- Update CalendarSyncService.cancelBooking to pass skipCalendarSyncTaskCreation: true
- Update CalendarSyncService.rescheduleBooking to pass skipCalendarSyncTaskCreation: true
- Update tests to verify the flags are passed correctly
This prevents infinite loops when calendar events are cancelled/rescheduled from
external calendars (Google/Office365) which trigger webhooks to Cal.com, which
would otherwise try to update the external calendar again.
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: rename flag to skipCalendarSyncTaskCancellation and use static imports
- Rename skipCalendarSyncTaskCreation to skipCalendarSyncTaskCancellation in handleCancelBooking
- Convert dynamic imports to static imports in CalendarSyncService
- Update tests to use vi.hoisted for proper mock hoisting with static imports
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add error handling and static import for handleNewBooking
- Create index.ts entry point for handleNewBooking directory
- Add try-catch error handling to cancelBooking and rescheduleBooking
- Log errors but don't block calendar sync operations
- Update tests to verify errors are caught and logged, not thrown
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: use RegularBookingService directly and fix safeStringify usage
- Remove handleNewBooking/index.ts and call getRegularBookingService().createBooking() directly in CalendarSyncService
- Fix safeStringify usage: apply to error itself, not wrapper object
- Update tests to mock getRegularBookingService instead of handleNewBooking
Addresses PR comments from Volnei and Cubic-dev-ai
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: restore handleNewBooking/index.ts and fix bookingData structure for calendar sync
- Restore handleNewBooking/index.ts as entry point for static imports
- Fix CalendarSyncService.rescheduleBooking to use correct bookingData structure
with required fields (eventTypeId, start, end, timeZone, language, metadata)
- Use rescheduleUid to indicate this is a reschedule operation
- Fix safeStringify usage in error logging (wrap only the error, not the whole object)
- Update tests to match new bookingData structure
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: address PR comments - use RegularBookingService directly, remove unnecessary flags, fix booking reference update
- Remove handleNewBooking/index.ts wrapper and use getRegularBookingService().createBooking() directly in CalendarSyncService
- Remove allRemainingBookings and cancelSubsequentBookings flags from cancelBooking (only cancel the specific booking, not the entire series)
- Move bookingReference update outside skipCalendarSyncTaskCancellation block for data consistency
- Update tests to match new implementation
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* chore: remove unnecessary comment from zod-utils.ts
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add Sentry metrics telemetry and fix null assertions in CalendarSyncService
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: use dynamic import for getRegularBookingService to avoid RAQB import in server context
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: use dynamic import for findTeamMembersMatchingAttributeLogic to avoid RAQB import in server context
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* improve booking update
* add more tests and edge cases
* fix: add required actionSource and Sentry mocks to calendar sync
Add actionSource: "SYSTEM" to handleCancelBooking call after it became
required, and mock @sentry/nextjs in test files.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: hide branding for teams
* fix: remove unused organizationId and username fields from profiles select
Addresses Cubic AI review feedback (confidence 9/10) to select only
the profile fields that are actually used. The organizationId and
username fields were fetched but never referenced in this function.
Co-Authored-By: unknown <>
* fix: unit tests
* fix: add prisma named export to test mock
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* Add tests: packages/features/profile/lib/hideBranding.test.ts
Generated by Paragon from proposal for PR #27643
* chore: implement cubic feedback
* fix: merge conflicts
* fix: unit tests
* fixup
* refactor: implement DI pattern for event type service
* fix: atoms build
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: workflow auto translation
* tests: add unit tests
* refactor: tests and workflow
* fix: type err
* fix: type err
* fix: remove redundant index on WorkflowStepTranslation
The @@index on [workflowStepId, field, targetLocale] duplicates the @@unique
constraint on the same columns. A unique index already provides efficient
lookups, so the separate @@index adds storage overhead and write latency
without benefit.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* fix: correct locale mapping when translation API returns null
Map translations with their corresponding locales before filtering to
preserve correct locale-to-translation associations. Previously, filtering
out null translations would reindex the array, causing incorrect locale
mappings when any translation in the batch failed.
Also fixes pre-existing lint warnings:
- Move exports to end of file
- Add explicit return type to processTranslations
- Replace ternary with if-else for upsertMany selection
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: address review feedback for workflow auto-translation
- Add change detection before creating translation tasks
- Rename userLocale to sourceLocale in task props for clarity
- Show source language in UI with new translation key
- Extract SUPPORTED_LOCALES to shared translationConstants.ts
- Fix locale mapping bug in translateEventTypeData.ts
- Add WhatsApp translation support
- Abstract translation lookup into shared translationLookup.ts helper
- Restore if-else readability for SCANNING_WORKFLOW_STEPS
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-Authored-By: unknown <>
* fix: update test to use sourceLocale instead of userLocale
Co-Authored-By: unknown <>
* refactor: feedback
* fix: handle first time
* fix: tests
* fix: tests
* fix: address Cubic AI review feedback (confidence 9/10 issues)
- WhatsApp translation: Apply variable substitution using getSMSMessageWithVariables
and clear contentSid when using translated body to ensure Twilio uses the
translated text instead of the original template
- update.handler.ts: Change sourceLocale assignment from ?? to || for consistency
with tasker payload behavior (line 481)
- ITranslationService.ts: Rename methods from plural to singular naming:
- getWorkflowStepTranslations -> getWorkflowStepTranslation
- getEventTypeTranslations -> getEventTypeTranslation
Updated all call sites and tests accordingly
Co-Authored-By: unknown <>
* fix: address Cubic AI review feedback (confidence 9/10+ issues)
- Fix getSMSMessageWithVariables to handle WHATSAPP_ATTENDEE action for
locale and timezone (confidence 9/10)
- Remove WhatsApp translation feature that set contentSid to undefined
since Twilio ignores body parameter for WhatsApp and requires
pre-approved Message Templates (confidence 10/10)
Co-Authored-By: unknown <>
* fix: translatio
* Add tests: packages/features/eventTypeTranslation/repositories/EventTypeTranslationRepository.test.ts
Generated by Paragon from proposal for PR #27087
* Add tests: packages/features/tasker/tasks/translateWorkflowStepData.test.ts
Generated by Paragon from proposal for PR #27087
* chore: nit
* chore: verfied atg
* fix: set sourceLocale for new steps, add shouldDirty to checkbox, remove spec docs
- Set sourceLocale fallback in addedSteps mapping to fix stale detection mismatch
- Add { shouldDirty: true } to autoTranslateEnabled checkbox onChange
- Remove specs/workflow-translation/ directory (planning docs, not for repo)
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
Co-Authored-By: unknown <>
* chore: add specs back
* fix: type error
* fix: type error
* fix: type err
* fix: tests
* refactor: feedback
* fix: type err
* refactor
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
* perf: improve getTotalBookingDuration query
* perf: improve getTotalBookingDuration query
* test: add unit tests for getTotalBookingDurationForUsers and yearly duration pre-fetching
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: integration tests
* fix: update tests
* fix: use vi.stubEnv instead of direct process.env modification for test isolation
Replace direct deletion of process.env variables with vi.stubEnv() to ensure
automatic restoration after tests, preventing flaky tests and side effects
in parallel execution.
Issue identified by Cubic AI (confidence: 9/10)
Co-Authored-By: unknown <>
* chore: add log
* refactor: imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add no users found fallback action for routing forms
- Add NoUsersFoundFallbackActionType enum with CustomPageMessage and ExternalRedirectUrl options
- Add noUsersFoundFallbackAction field to zodNonRouterRoute schema
- Add NoUsersFoundFallback UI component in RouteBuilder for configuring fallback action
- Update handleResponse to return noUsersFoundFallbackAction when no team members match
- Add i18n translation keys for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: change fallback to use same action options as main route
- Replace noUsersFoundFallbackAction with fallbackAction that has same structure as main action
- Add action dropdown and input fields to fallback attributes query builder section
- Support Event redirect, External URL, and Custom page options in fallback
- Remove separate NoUsersFoundFallback toggle component
- Update handleResponse to return fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure fallbackAction type is always defined in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add guards for route.fallbackAction in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only return fallbackAction when no users are found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: restructure fallback section to mirror main route structure
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure backwards compatibility for existing routes with fallbackAttributesQueryValue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction with main event type for backwards compatibility
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include fallbackAction in getRoutesToSave to persist changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use fallbackAction in getRoutedUrl when no team members found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use i18n for fallback label and add tests for fallback action
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: do not trigger fallback action when CRM contact owner is found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: prioritize fallbackAction over fallbackAttributesQueryValue
When a route has a fallbackAction configured, skip the
fallbackAttributesQueryValue to ensure the fallbackAction is triggered
when no team members are found. This maintains backwards compatibility
by only using fallbackAttributesQueryValue when fallbackAction is not set.
Also treats teamMemberIdsMatchingAttributeLogic being null (routing couldn't
run, e.g., missing orgId) the same as an empty array for the purpose of
triggering the fallbackAction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract RouteActionSelector shared component for action selection UI
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add i18n string for default custom page message
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* revert: remove webhook trigger changes (to be moved to separate PR)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add ROUTING_FORM_FALLBACK_HIT to PayloadBuilderFactory trigger mapping
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* style: apply biome formatting fixes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: restore removed comments in RouteBuilder
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add prisma migration for ROUTING_FORM_FALLBACK_HIT enum value
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add ROUTING_FORM_FALLBACK_HIT to FormTriggerEvents type union
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add test coverage for triggerFallbackWebhook function
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: consolidate fallback webhook firing with form submission webhooks
Move ROUTING_FORM_FALLBACK_HIT webhooks into the same Promise.all() batch
as FORM_SUBMITTED / FORM_SUBMITTED_NO_EVENT, eliminating redundant
getWebhooks/getWebhookTargetEntity/getOrgIdFromMemberOrTeamId calls.
- Add fallbackAction parameter to _onFormSubmission and onSubmissionOfFormResponse
- Remove triggerFallbackWebhook function and all its usages/imports
- Move getFallbackAction() earlier in handleResponse.ts and pass result through
- Update all tests to verify batched webhook behavior
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add UI option for trigger
* feat: persist fallbackAction on queued form responses
When a routing form response is queued, the fallbackAction determined
during routing is now stored on the QueuedFormResponse record. When
the queued response is later converted to a real FormResponse, the
stored fallbackAction is passed to onSubmissionOfFormResponse so the
ROUTING_FORM_FALLBACK_HIT webhook fires correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add coverage for fallbackAction in queued form response flow
Verify that fallbackAction is persisted when queuing and passed through
to onSubmissionOfFormResponse when the queued response is converted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace request.url with WEBAPP_URL from @calcom/lib/constants as the base URL
for NextResponse.redirect() in booking confirmation API routes. Behind a reverse
proxy, request.url resolves to http://localhost:3000 instead of the public domain.
Fixes#20358
Co-Authored-By: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* SMS reminders not trigger
* addressed devin review
* Add tests: packages/features/ee/workflows/lib/service/scheduleLazySMSWorkflow.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/ee/workflows/lib/reminders/reminderScheduler.smsLazy.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/tasker/tasks/sendWorkflowSMS.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/ee/workflows/lib/reminders/smsReminderManager.test.ts
Generated by Paragon from proposal for PR #27942
* Delete packages/features/ee/workflows/lib/reminders/reminderScheduler.smsLazy.test.ts
* fix: update sendWorkflowSMS tests to match refactored implementation
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: use smsReminderNumber fallback for attendee phone in SMS workflows
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* revert: keep only scheduleSMSReminders.ts changes, revert all other files
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: restore reminderScheduler.ts and smsReminderManager.ts to original branch state
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: use smsReminderNumber fallback for attendee phone in WhatsApp reminders
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add strategyName property to ISeatBillingStrategy interface and all implementations
- Log which strategy handled the invoice.upcoming webhook (not just whether it applied)
- Always log strategy result (not just when applied=true) for better observability
- Rename highWaterMarkApplied to strategyApplied since applied covers multiple strategies
Co-Authored-By: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(caldav): use consistent UIDs and inject VTIMEZONE in iCalendar output
Two remaining CalDAV interop issues from #9485:
1. UID consistency: use the booking's canonical UID (event.uid) instead
of always generating a new random UUID. CalDAV servers use UID as the
event identifier — different UIDs cause duplicate calendar entries.
2. VTIMEZONE injection: the ics library generates UTC times with no
VTIMEZONE block. CalDAV servers like Fastmail read this as UTC and
send scheduling emails with wrong times. Per RFC 5545 §3.6.5,
DTSTART with TZID requires a matching VTIMEZONE component. We now
build a proper VTIMEZONE using binary-searched DST transitions for
the event's year, handling Northern/Southern hemisphere correctly.
* fix: use pre-transition offset for VTIMEZONE DTSTART per RFC 5545
The DTSTART in VTIMEZONE components must represent the local time
interpreted with the pre-transition offset (TZOFFSETFROM), not the
post-transition offset. For example, US Eastern spring forward DTSTART
should be 02:00 (EST), not 03:00 (EDT).
* Remove comments on UID handling in createEvent
Removed comments about UID handling for calendar events.
* Revise injectVTimezone documentation
Update injectVTimezone function documentation to clarify UTC handling.
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: use import.meta.env instead of process.env in embed.ts
Using import.meta.env is Vite's native way to handle environment variables
in browser bundles. This prevents biome from auto-adding 'import process
from node:process' which breaks the embed.js in browsers.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Remove unncessar comment
* fix: migrate remaining process.env to import.meta.env in embed.ts
Complete the migration started by @hariombalhara by also converting
process.env usages at lines 1681, 1682, and 1700 to import.meta.env.
Update env.d.ts to include EMBED_PUBLIC_EMBED_FINGER_PRINT,
EMBED_PUBLIC_EMBED_VERSION, and INTEGRATION_TEST_MODE types.
This ensures no process.env references remain in embed.ts, fully
preventing Biome from auto-adding import process from node:process.
Co-Authored-By: unknown <>
* fix: add biome overrides for embed-core to prevent auto-import of node:process
Reverts import.meta.env back to process.env since API V2's tsconfig uses
module: commonjs which doesn't support import.meta. Instead, adds biome
overrides for embed-core to disable useNodejsImportProtocol, noProcessEnv,
and noProcessGlobal rules that were causing biome to auto-add
'import process from node:process' on save.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update routing form identifier hint text with learn more link
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* UI improvements
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* fix: prevent deselecting active option in ToggleGroup
Radix UI's single-type ToggleGroup allows deselection by default,
calling onValueChange("") when the active item is clicked.
Fix by converting to controlled mode with proper dual-mode support:
- Controlled (value prop): parent owns state, component just filters
empty values from onValueChange. Parent can still reject changes.
- Uncontrolled (defaultValue prop): internal useState prevents
deselection by only updating state for non-empty values.
* Update ToggleGroup.tsx
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
* fix: add CSRF protection to OAuth callback via HMAC-signed nonce
The OAuth state parameter was used only for passing application data
(returnTo, teamId) with no cryptographic binding to the user session.
An attacker could authorize their own account on a provider, capture the
authorization code, and trick a logged-in user into visiting the callback
URL to link the attacker's account to the victim's Cal.com profile.
Changes:
- encodeOAuthState: generate a random nonce and HMAC-sign it with
NEXTAUTH_SECRET + userId, injecting both into the OAuth state
- decodeOAuthState: verify the HMAC on callback using timingSafeEqual;
skip verification when nonce is absent (backwards compatible with apps
that don't yet use encodeOAuthState)
- Stripe callback: replace raw state.returnTo redirect with
getSafeRedirectUrl to prevent open redirect, remove redundant
getReturnToValueFromQueryState, add missing return on access_denied
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make CSRF nonce verification mandatory with allowlist for exempt apps
Makes nonce/HMAC verification mandatory by default in decodeOAuthState,
preventing attackers from bypassing CSRF protection by omitting nonce
fields from the state parameter.
Apps not yet migrated to encodeOAuthState (stripe, basecamp3, dub,
webex, tandem) are explicitly allowlisted and pass their slug to
decodeOAuthState to skip verification.
Addresses review feedback (identified by cubic) about the conditional
check being trivially bypassable.
Co-Authored-By: unknown <>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Replace hardcoded color with semantic text-success class
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove color class completely from display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add valuesWithParams support for translatable complex field values
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor(booking-audit): use discriminated union for displayFields and update consumers
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add explicit return type to getBookingHistoryHandler to bust stale tRPC build cache
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: replace $t() nested interpolation with separate translation keys and add translationsWithParams tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(meta-pixel): add trackingEvent to zod schema
* fix: do not allow trackingEvent to be optional
* feat(meta-pixel): add trackingEvent selection dropdown to UI
* feat(meta-pixel): add trackingEvent to Event Type App Data
* fix(meta-pixel): implement history API hook for SPA conversion tracking
* fix(meta-pixel): check for cancel in URL and prevent Re-wrapping of pushState
* Update EventTypeAppSettingsInterface.tsx
* fix: make trackingEvent optional in zod schema to fix type-check
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
* feat: add keyboard shortcuts and tooltips to booking slideover buttons
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: remove duplicate useBookingLocation import and fix import ordering
- Removed duplicate import of useBookingLocation from non-existent
@calcom/web/modules/bookings/hooks/useBookingLocation path
- Fixed import ordering to satisfy biome organizeImports rules
- Removed unnecessary code comment
- Original feature by @PeerRich via Devin AI
Co-Authored-By: unknown <>
* use same style arrows for both button and remove bydefault focus from them
* fix join button tooltip hover
* fix: disable keyboard shortcuts when overlays/dialogs are open on BookingDetailsSheet
Co-Authored-By: unknown <>
* fix: use focus-based detection instead of selector-based overlay detection for keyboard shortcuts
Co-Authored-By: unknown <>
* fix: allow keyboard shortcuts when focus is on sheet ancestors
Co-Authored-By: unknown <>
* fix: use capture phase for keyboard handler to prevent Enter from activating focused buttons
Co-Authored-By: unknown <>
* fix: allow shortcuts when focus is on page elements outside any Radix portal
Co-Authored-By: unknown <>
* fix: handle calendar event clicks in onInteractOutside to prevent sheet close/reopen
Co-Authored-By: unknown <>
* fix: stop arrow key propagation to prevent Radix dropdown from opening during booking navigation
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: always stop arrow key propagation when sheet is active, even at first/last booking
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: extract keyboard handler into testable utility with tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: resolve type errors in keyboard handler config and test mocks
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: add e2e tests for booking sheet keyboard shortcuts
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: replace text= locators with data-testid selectors in e2e tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* made 3 fixes: Fix 1 — Stabilize handleNext/handlePrevious/handleClose with useCallback
In BookingDetailsSheet.tsx, all three handler functions were plain arrow functions recreated on every render, causing the useEffect to tear down and re-attach the document keydown listener unnecessarily. Wrapped all three in useCallback with proper dependency arrays (the Zustand store functions they call).
Fix 2 — data-booking-list-item verified (no change needed)
Confirmed that data-booking-list-item is rendered on BookingListItem.tsx and data-booking-calendar-event is rendered on Event.tsx. The onInteractOutside handler in the final merged state correctly checks both selectors. No code change required.
Fix 3 — Removed dead code from JoinMeetingButton
Reverted JoinMeetingButton back to a plain function component:
Removed forwardRef wrapping (no caller passes a ref)
Removed showTooltip prop (unused — tooltip is handled by the parent BookingDetailsSheet)
Removed ref prop from the inner Button
Removed unused Tod forwardRef imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Co-authored-by: eunjae@cal.com <hey@eunjae.dev>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
* init
* wiring up
* fix type
* feat: implement DI pattern for webhook producer in API v2
- Export IWebhookProducerService and getWebhookProducer from platform-libraries
- Add WEBHOOK_PRODUCER token and useFactory provider in RegularBookingModule
- Inject webhookProducer in RegularBookingService and pass to base class
This follows the composition root pattern where only the NestJS module
knows about getWebhookProducer(), and all consumers depend only on the
IWebhookProducerService interface via constructor injection.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* test: migrate BOOKING_REQUESTED tests to new webhook architecture
- Remove failing BOOKING_REQUESTED tests from fresh-booking.test.ts (4 tests)
- Remove failing BOOKING_REQUESTED tests from reschedule.test.ts (2 tests)
- Remove failing BOOKING_REQUESTED test from collective-scheduling.test.ts (1 test)
- Replace WebhookTaskConsumer.test.ts with placeholder (constructor changed)
- Create new webhook architecture test suite:
- producer/WebhookTaskerProducerService.test.ts (14 tests)
- consumer/WebhookTaskConsumer.test.ts (8 tests)
- consumer/triggers/booking-requested.test.ts (8 tests)
The new test suite is organized by trigger type for extensibility as more
triggers are migrated to the producer/consumer pattern.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* test: remove paid events BOOKING_REQUESTED test (moved to new architecture)
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* wrap webhook in own try-catch
* wire datafetcher
* fix
* fix v2
* fix circular dependency
* --
* merge-conflict-resolve
* mreg-conflict-resolve
* remove early return
* test: add integration tests for BOOKING_REQUESTED webhook producer invocation
Cover all 8 scenarios verifying the booking flow correctly invokes
the webhook producer for BOOKING_REQUESTED:
1. Basic confirmation → queueBookingRequestedWebhook called
2. Booker-is-organizer + confirmation → still called
3. Confirmation threshold NOT met → not called (BOOKING_CREATED instead)
4. Confirmation threshold IS met → called
5. Paid event + confirmation → called after payment succeeds
6. Reschedule + confirmation (non-organizer) → called (not BOOKING_RESCHEDULED)
7. Reschedule + confirmation (organizer) → not called (BOOKING_RESCHEDULED instead)
8. Collective scheduling + confirmation → called
Adds reusable MockWebhookProducer helper in @calcom/testing for
extendable use as more webhook triggers migrate to the new architecture.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix bug
* fix conditional check
* remove unnecessary comment
* add missing expect
* remove empty test
* clean up
* tasker config
* --
* fix missing metadata
* remove faulty if else
* test: add payload content verification tests for BOOKING_REQUESTED webhook
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* remove unnecessary tests
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
* feat: replace admin API settings page with link to API docs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: remove unused AdminAPIView component
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: rename admin API tab to API Docs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: change URL prefill behavior to skip only invalid fields
Previously, when prefilling booking form fields from URL query parameters,
if any single field failed validation, all prefilling was skipped and an
empty object was returned.
This change modifies the behavior so that only invalid fields are skipped,
while valid fields are still prefilled. The implementation:
- Adds validateSingleFieldForPrefill() to validate individual fields
- Adds parseFieldByField() to collect valid fields when full parse fails
- Modifies getBookingResponsesPartialSchema to use field-by-field parsing
as a fallback when full schema validation fails
- Logs which specific fields were skipped and why
This improves user experience by preserving valid prefill data even when
some fields have invalid values.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: reduce code duplication in partial prefill validation
Simplify the implementation by integrating field-by-field error handling
directly into the existing preprocess() function instead of using separate
helper functions.
Changes:
- Remove validateSingleFieldForPrefill() and parseFieldByField() functions
- Simplify getBookingResponsesPartialSchema to directly use preprocess()
- Add try-catch wrapper in preprocess() when isPartialSchema=true
- Continue processing other fields even if one fails during preprocessing
- Log skipped fields with reasons at debug level
This reduces code duplication while maintaining the same behavior of
skipping only invalid fields during URL prefill.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* refactor: rename getRefinedValue to superRefineField and add edge case tests
- Rename getRefinedValue to superRefineField for clarity
- Remove duplicate multiselect test block
- Add test for empty object name field during partial prefill
- Add test for invalid URL format during partial prefill
- Add test for null responses during partial prefill
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Simplifu
* Simplif more
* Enhance booking responses schema tests to validate required fields and name variants. Updated error handling for empty names and added tests for first and last name requirements. Refactored schema validation logic to improve clarity and maintainability.
* fix types
* test: add test for email validation behavior during partial prefill
Documents that email validation is relaxed to z.string() during partial prefill,
consistent with phone field behavior where partial values like '+91' are accepted.
This addresses Cubic AI feedback (confidence 9/10) to verify email handling.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix
* Add tests: packages/features/bookings/lib/getBookingResponsesSchema.test.ts
Generated by Paragon from proposal for PR #26982
* Revert "Add tests: packages/features/bookings/lib/getBookingResponsesSchema.test.ts"
This reverts commit fc2b7ad7ca8099bc4f68e97b383ea16b41cdd772.
* Simplify
* fix: sanitize partial prefill log to avoid exposing user input data
Replaced raw Zod issues array in console.warn with a count of
validation errors to prevent sensitive user data from being logged.
Issue identified by cubic (confidence 9/10).
Co-Authored-By: unknown <>
* fix
* Remove throw Error
* Refactor booking responses schema tests to use helper functions for assertions
- Introduced `expectResponsesToBe` and `expectParsingToFail` helper functions to streamline test assertions.
- Replaced direct assertions with these helper functions for improved readability and maintainability.
- Removed redundant error handling code to simplify test logic.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add duplicate functionality for managed event types
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat: add disclaimer and redirect for managed event type duplication
- Add info alert disclaimer in duplicate modal for managed event types
- Pass schedulingType to duplicate dialog via query params
- Redirect to assignment tab after duplicating managed event types
- Add translation key for managed event type duplicate disclaimer
Co-Authored-By: peer@cal.com <peer@cal.com>
* test: add tests for managed event type duplication
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: replace PrismaMock unit test with Prisma integration test for duplicate handler (#27973)
* refactor: replace PrismaMock unit test with Prisma integration test for duplicate handler
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: re-throw TRPCErrors in duplicate handler catch block and assert specific error codes in tests
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
* fix: allow booking rejection via GET for email clients that do not support forms
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* Refactor test cases for GET handler in route.test.ts
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Replace 3 sequential queries (with expensive attendee email IN clauses)
with 2 parallel queries using indexed eventType.teamId filtering and
in-application user filtering with Sets for O(1) lookups.
Fix managed events booking-limit behavior: managed bookings now only
match by organizer userId (not attendee email), preserving the original
query semantics where managed bookings used userId-only matching.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Fix exclusion filter - include all team members
* Fix display when members aren't saved in the DB
* Update tests
* test: add missing negation operator tests for TEXT, NUMBER, and compound rules
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert non-intentional changes to AddMembersWithSwitch.tsx
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: decouple large calendar from features
* fix: move calendar to features since this is the better approach
* chore: move OOO slots to features
* feat: add DefaultOutOfOfficeSlot fallback for calendar OOO rendering
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* chore: cleanup unused LargeCalendar component
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Replaced dayjs with date-fns for better performance and consistency in date handling. Added Tooltip for displaying formatted timestamps and improved search functionality by translating field labels and values. Cleaned up code for better readability.
* fix: deep link reschedule audit log to booking drawer history tab
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: make booking drawer tab-agnostic for cross-tab deep links
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use status-agnostic /bookings URL for audit log deep links
- Update audit service URLs from /bookings/upcoming?uid=... to /bookings?uid=...
- Add /bookings/page.tsx redirect that routes to /bookings/upcoming preserving query params
- Update tests to expect new URL format
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* refactor: use client-side replaceState instead of server redirect for booking deep links
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use router.replace instead of replaceState to update tab and booking list
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* improvements
* refactor: extract deep link logic from BookingListContainer into usePreSelectedBooking hook
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use proper BookingOutput status type in test helper
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: decouple getTabForBooking from BookingOutput type for simpler testing
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: accept Date | string for endTime in BookingForTabResolution interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: eliminate initialBookingUid prop drilling and revert formatting-only changes
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use AuditDeepLink wrapper to preserve target=_blank through ServerTrans cloneElement
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Improve code organization
* refactor: rename usePreSelectedBooking to useSwitchToCorrectStatusTab
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: push preSelectedBooking into store so drawer opens on direct navigation
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* fixes
* fixes
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: revert assignmentReason breaking change in webhook payloads
Remove the new { category, details } format from EventPayloadType to
maintain backward compatibility for webhook consumers. The new format
is stripped at all webhook payload construction sites by destructuring
assignmentReason out of CalendarEvent before spreading into the payload.
A sanitizeAssignmentReasonForWebhook function provides an additional
safety net in sendPayload itself. Emails and booking single view
continue to use the new format via CalendarEvent.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: strip assignmentReason from handlePaymentSuccess webhook payload
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: strip assignmentReason from triggerWebhooks and handleSeats webhook payloads
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* refactor: use zod safeParse instead of type assertion in sanitizeAssignmentReasonForWebhook
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add configurable landing page feature
- Add useUserPreferences hook for persistent storage of landing page preference
- Add LandingPagePicker component for both iOS and Android/web platforms
- Update tabs index to redirect based on user preference
- Update bookings index to accept initial filter from URL params
- Add App Settings section in More screen with landing page selector
- Clear user preferences on logout for fresh state
* fix(companion): remove try-finally for React Compiler compatibility
* fix(companion): use router.replace for dynamic landing page redirect
Replace Redirect component with router.replace() to fix TypeScript
strict typing issue with expo-router's Href type for dynamic routes.
* fix(companion): use literal route strings for TypeScript strict typing
Use switch statement with literal route strings instead of dynamic
string variable to satisfy expo-router's strict Href type checking.
* working fix
* better css
* refactor: apply biome formatting to small packages + packages/lib
Format packages/sms, packages/prisma, packages/platform/libraries,
packages/platform/examples, packages/platform/types, packages/emails,
and packages/lib.
Excludes packages/platform/examples/base/src/pages/[bookingUid].tsx
due to pre-existing lint errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* revert: remove packages/platform formatting changes
Revert biome formatting for packages/platform as requested.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: enable onboarding-v3 globally and update e2e tests
- Add migration to set onboarding-v3 feature flag enabled=true
- Rewrite onboarding.e2e.ts for v3 flow (Plan Selection → Personal Settings → Calendar)
- Update URL assertions in signup, team-invitation, org-invitation, ab-tests-redirect, and auth tests to accept both old and new onboarding paths
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: use pathname-only matching in waitForURL to prevent false matches on callbackUrl query param
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* use test IDs
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* feat: add signup watchlist review feature flag and handler logic
- Add 'signup-watchlist-review' global feature flag
- Add SIGNUP to WatchlistSource enum in Prisma schema
- When flag enabled, lock new signups and add email to watchlist
- Show 'account under review' message on signup page
- Add i18n strings for review UI
- Create seed migration for the feature flag
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* test: add isAccountUnderReview tests to fetchSignup test suite
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: address Cubic AI review feedback (confidence >= 9/10)
- Remove 'import process from node:process' in signup-view.tsx (P0 bug in 'use client' component)
- Move watchlist review check before checkoutSessionId early return in calcomSignupHandler (P1 premium bypass)
- Revert selfHostedHandler to original state (out of scope per user request)
- Add test mocks for FeaturesRepository and GlobalWatchlistRepository
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: remove node:process import from useFlags.ts (client-side file)
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: remove !token condition from watchlist review check
Token is present in normal email-verified signups, so the !token
condition was incorrectly skipping watchlist review for verified users.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* Apply suggestion from @cubic-dev-ai[bot]
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* refactor: move user lock to UserRepository.lockByEmail
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: use cached getFeatureRepository() instead of deprecated FeaturesRepository
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: remove user locking, keep only watchlist addition on signup review
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: lock user on signup review, remove watchlist entry on unlock
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Pruned the 'License' section and Pushed it before 'Acknowledgement' to avoid confusion and Clarity
* Improves README file by pruning License Section and Aligning Properly
* Remove License section from README.md
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: update data-table hook imports after refactor (#27833)
The data-table hooks (useDataTable, useFilterValue) were moved from
@calcom/features/data-table to ~/data-table/hooks/ in #27833 but this
file was missed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update DataTableProvider import path in insights-wrong-routing-view
Another import missed in #27833 refactor — DataTableProvider moved from
@calcom/features/data-table/ to ~/data-table/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* factory and statergie
* chore: use correct method of DI
* feat: add onchagne
* add logic to HWM stat
* add webhook resolver methods to each statergy
* move seat tracking + webhooks over to own statergy
* Move to factory base approach
* move logic to correct class
* rename create -> createByTeamId
* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED
Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.
Identified by cubic (https://cubic.dev)
Co-Authored-By: unknown <>
* feat: active user billing
* add tests
* UI side for users on active billing
* use correct period of stripe sub
* feat: claude feedback
* fix: skip Stripe sync for canceled/expired subscriptions to prevent repeated API calls
Co-Authored-By: unknown <>
* feat: feedback
* feat: only render when active users mode is set
* fix type error
* fix: constants + feature flags
* fix: default to null in tests
* chore: use seats in test
* fix: use node:crypto protocol for Node.js builtin imports
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Fixes footer text overlap in the Booking Details drawer where action buttons covered the last content items on smaller screens.
**Root cause:** `sm:-my-6` on `SheetFooter` applied both negative top and bottom margins. The negative top margin pulled the footer up into the body content area, causing the overlap.
**Fix:**
- Removed `-mb-4` and `sm:-my-6` from `SheetFooter`, keeping only horizontal negative margins (`-mx-4` / `sm:-mx-6`) for edge-to-edge stretch
- Added `pb-0` to `SheetContent` to remove bottom padding so the footer sits flush at the bottom on all breakpoints
<img width="1098" height="1362" alt="image" src="https://github.com/user-attachments/assets/0418df56-a432-4edf-8e5b-664a5e8c18ac" />
- Fixes #XXXX (GitHub issue number)
- Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)
## Visual Demo (For contributors especially)
#### Image Demo (if applicable):
See screenshot above showing the overlap bug. The footer actions ("..." button) overlap with the last booking history entry.
## Mandatory Tasks (DO NOT REMOVE)
- [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). If N/A, write N/A here and check the checkbox.
- [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Open any booking in the bookings list to open the details drawer
2. Scroll to the bottom of the drawer content
3. Verify the footer actions (buttons, dropdown) do not overlap with the last content item
4. Test on both mobile and desktop viewport sizes — footer should sit flush at the bottom with no extra gap on either
## Human Review Checklist
- [ ] Verify footer appearance on **mobile** (below `sm` breakpoint) — no gap below footer, no overlap with content
- [ ] Verify footer appearance on **desktop** (`sm+` breakpoint) — same checks
- [ ] Confirm the footer still stretches edge-to-edge horizontally on both breakpoints
## Checklist
- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my changes generate no new warnings
- My PR is too large (>500 lines or >10 files) and should be split into smaller PRs
---
Link to Devin run: https://app.devin.ai/sessions/b9d130b8a7a54f039b90b53921b19c78
Requested by: @hariombalhara
* Add DB table for wrong assignment reports
* When report is submitted write to the db
* Prevent duplicate reportings
* test: add migration and tests for WrongAssignmentReport table
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add unique constraint on bookingUid and booking access check for hasWrongAssignmentReport
- Add @unique constraint on bookingUid in WrongAssignmentReport model to prevent duplicate reports at DB level
- Add booking ownership check using BookingAccessService in hasWrongAssignmentReport endpoint
- Refactor hasWrongAssignmentReport into separate handler and schema files
Addresses Cubic AI review feedback on PR #27405
Co-Authored-By: unknown <>
* feat: add routingFormId to WrongAssignmentReport and fix Select clearing
- Add routingFormId field to WrongAssignmentReport model in schema.prisma
- Add relation to App_RoutingForms_Form with SetNull on delete
- Update WrongAssignmentReportRepository.createReport to accept routingFormId
- Update BookingRepository.findByUidIncludeEventTypeAndTeamAndAssignmentReason to include routedFromRoutingFormReponse
- Extract routingFormId from booking in reportWrongAssignment handler
- Fix Select clearing issue: handle null case when user clears team member selection
- Update tests to include routingFormId field
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add migration for routingFormId in WrongAssignmentReport
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add wrong assignment reports dashboard under routing tab
- Add reviewedById and reviewedAt fields to WrongAssignmentReport model
- Add repository methods for listing reports by status and updating status
- Create tRPC endpoints for fetching reports and updating status
- Create dashboard UI with pending/reviewed tabs showing routing form name
- Add translation keys for dashboard UI
- Integrate dashboard into routing insights page
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: move hooks before early return and fix indentation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address Udit's review comments
- hasWrongAssignmentReport: throw UNAUTHORIZED error instead of returning false
- reportWrongAssignment: add try-catch for Prisma P2002 unique constraint error
- WrongAssignmentReport: add Team relation to teamId field
- WrongAssignmentReportRepository: use findUnique instead of findFirst
- reportWrongAssignment: use i18n for error messages
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: revert hasWrongAssignmentReport to return false when user lacks access
Per PR checklist, hasWrongAssignmentReport should return { hasReport: false }
when user lacks access to booking, not throw an error. This allows the UI
to gracefully treat 'no access' as 'no report exists'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: update mocks for findUnique and i18n in unit tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: throw UNAUTHORIZED in hasWrongAssignmentReport and squash migrations
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: move wrong assignment reports to its own tab under Insights
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Use data table for wrong reports
* Add option to view routing trace
* feat: add view routing form submission action to wrong assignment reports
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add RoutingFormResponseSheet component for viewing form submissions
- Create slide-out sheet to display routing form responses
- Map option IDs to display labels for select/multiselect fields
- Handle both legacy and modern option formats
- Add i18n strings: form_submission, no_responses_found
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: improve wrong assignment reports UX
- Integrate RoutingFormResponseSheet as slide-out panel instead of new tab
- Fix dropdown padding by using StartIcon prop instead of manual Icon
- Allow direct status changes for reviewed reports (no need to reopen first)
- Remove unused Icon import
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: change User relation onDelete from Cascade to SetNull in WrongAssignmentReport
Address Hariom's review feedback:
- Changed reportedById from Int to Int? (nullable)
- Changed reportedBy relation from onDelete: Cascade to onDelete: SetNull
- Updated migration SQL to reflect these changes
This preserves wrong assignment reports even when the reporting user is deleted,
as the data is still useful for analysis.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add missing i18n strings for wrong assignment reports dashboard
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: move form response display value resolution server-side
Replace client-side option ID to label resolution in
RoutingFormResponseSheet with a new lean tRPC endpoint
(getFormResponseDisplay) that resolves values server-side using
the existing getHumanReadableFieldResponseValue utility. This
enforces DTO boundaries by returning a clean pre-resolved payload
instead of leaking internal option format details to the client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add report wrong assignment button to routing trace sheet
Wire up the WrongAssignmentDialog from the routing trace sheet header
so users can flag wrong assignments directly while viewing the trace.
The report button is disabled with a tooltip when a report already
exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: reuse existing WrongAssignmentDialog from parent
Replace the duplicate WrongAssignmentDialog in RoutingTraceSheet with
a callback to the existing instance in BookingActionsDropdown. This
reduces the prop surface from a 6-field reportContext object to an
onReport callback and hasExistingReport boolean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use repository instead of direct Prisma in getFormResponseDisplay
Replace direct Prisma query with PrismaRoutingFormResponseRepository's
findByIdIncludeForm method. Extend the method to also select form name,
description, userId, and teamId needed for display and auth checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: replace direct Prisma calls with repository methods
Use MembershipRepository.hasMembership() for auth checks and
TeamRepository.findAllByParentId() for child team queries instead
of direct Prisma calls. Replace direct user query with
UserRepository.getTimeZoneAndDefaultScheduleId(). Remove unused
seed script.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use PBAC services for wrong assignment report auth
Replace manual MembershipRepository.hasMembership() checks with
PBAC-aware permission checking. getWrongAssignmentReports uses
createTeamPbacProcedure middleware since teamId is in input.
updateWrongAssignmentReportStatus uses PermissionCheckService
directly since teamId is discovered from the report entity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve type errors in RoutingFormResponseSheet and wrong-routing view
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add seed script for wrong assignment reports test data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Revert "feat: add seed script for wrong assignment reports test data"
This reverts commit 0bd60e9661858a59aab1573d14d57d81733b7991.
* Only update reviewed fields when not pending
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: wrap handleStatusChange in useCallback to fix useMemo recalculation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Fix routing sheet UI
* fix: use appropriate error message in getFormResponseDisplay handler
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract WrongAssignmentReportService from tRPC handlers
Move business logic (booking lookup, duplicate check, report creation,
webhook dispatch, org-level team resolution) into a dedicated service
in packages/features. Handlers become thin controllers that only handle
auth checks and delegate to the service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restrict report status updates to admin/owner roles
Remove MembershipRole.MEMBER from fallbackRoles in
updateWrongAssignmentReportStatus permission check. Updating report
status is an administrative action that should be limited to team
admins and owners.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: localize hard-coded success message in WrongAssignmentReportService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: rename reviewed tab to handled and add missing translations
Rename the "Reviewed" tab to "Handled" since it groups three distinct
statuses (Reviewed, Resolved, Dismissed). Also add missing translation
keys for "resolved" and "dismissed" status badges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* merge: resolve conflicts with main branch
Merge main into devin/1769747741-wrong-assignment-dashboard, resolving conflicts in:
- BookingActionsDropdown.tsx: use main's booking prop with PR's fragment structure
- reportWrongAssignment.handler.ts: keep PR's service-based approach
- reportWrongAssignment.handler.test.ts: use main's class-based mocks with PR's additions
- WrongAssignmentReportService.ts: align with main's repo method and field names
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add guard when accessing assignmentReasonSortedByCreatedAt
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: address Cubic AI review feedback - select projection and useLocale refactor
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove stale TRPCError assertion in test (service throws ErrorWithCode)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: remove unused findByTeamIdAndStatus and findByTeamIdAndStatuses methods
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* perf: add composite index on WrongAssignmentReport and narrow findByIdIncludeForm select
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* perf: use lightweight findTeamIdById in update-status handler instead of findById
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: hariom@cal.com <hariombalhara@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: alex@cal.com <me@alexvanandel.com>
## What does this PR do?
Fixes an issue where underscores in form field identifiers were being stripped when converting to workflow template variables. According to the documentation, users should be able to use variables like `{COMPANY_NAME}` or `{MONTHLY_INFRASTRUCTURE_SPEND}`, but the regex in `formatIdentifierToVariable` was removing underscores, causing these variables to not match.
**The problem:**
- Form field identifier: `Company_Name` or `Monthly_Infrastructure_Spend`
- Expected variable: `{COMPANY_NAME}` or `{MONTHLY_INFRASTRUCTURE_SPEND}`
- Actual variable (before fix): `{COMPANYNAME}` or `{MONTHLYINFRASTRUCTURESPEND}`
**The fix:**
- Updated the regex from `[^a-zA-Z0-9 ]` to `[^a-zA-Z0-9_ ]` to preserve underscores in `formatIdentifierToVariable`
- Added a private `formatIdentifierToVariableLegacy` function that maintains the old behavior (strips underscores)
- Added `getVariableFormats` helper that returns both current and legacy formats for backward compatibility
- Added `convertResponsesToVariableFormats` helper in `executeAIPhoneCall.ts` to convert form responses to both variable formats
- Updated `customTemplate.ts` matching logic to support both variable formats
- Updated `executeAIPhoneCall.ts` to include both variable formats when building dynamic variables
This ensures existing templates using `{COMPANYNAME}` continue to work while new templates can use the documented `{COMPANY_NAME}` format.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - no docs changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Create a routing form with a field identifier containing underscores (e.g., `Company_Name`)
2. Create a workflow with a template using `{COMPANY_NAME}` (with underscore)
3. Submit the form and verify the variable is replaced correctly
4. Also test with `{COMPANYNAME}` (without underscore) to verify backward compatibility
**Automated tests added:**
- `customTemplate.test.ts` - 15 tests covering `formatIdentifierToVariable`, `getVariableFormats`, and template variable replacement
- `executeAIPhoneCall.test.ts` - 6 tests covering `convertResponsesToVariableFormats` function for form responses
## Human Review Checklist
- [ ] Verify the regex change `[^a-zA-Z0-9_ ]` correctly preserves underscores
- [ ] Verify backward compatibility: both `{COMPANY_NAME}` and `{COMPANYNAME}` should work for the same form field
- [ ] Verify `convertResponsesToVariableFormats` correctly generates both variable formats
- [ ] Review test coverage for edge cases (empty responses, undefined values)
---
Link to Devin run: https://app.devin.ai/sessions/9d5d90178714493086d94691865c3e07
Requested by: @hariombalhara
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/calcom/cal.com/pull/27571">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin">
</picture>
</a>
<!-- devin-review-badge-end -->
* feat: re-render bookings page after feature opt-in without full refresh
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: make onOptInSuccess a required prop
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: maintain uid parameter in BookingDetailsSheet URL
Move the isSyncedFromUrlToStoreRef guard from the effect level to inside
the Zustand subscription callback. This ensures the Store→URL subscription
is always created while still preventing premature URL overwrites during
URL→Store sync.
Also adds an E2E test verifying the uid parameter appears in the URL
after clicking a booking item in the v3 bookings view.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Update apps/web/playwright/bookings-list.e2e.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: clean up bookings-v3 feature flag after E2E test
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* refactor: apply biome formatting to packages/features (batch 1 - small subdirs)
Format small subdirectories in packages/features: di, flags, holidays, oauth,
settings, users, assignment-reason, selectedCalendar, hashedLink, host, form,
form-builder, availability, data-table, pbac, schedules, troubleshooter,
eventtypes, calendar-subscription, and root-level files.
Also includes straggler apps/web BookEventForm.tsx.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 2 - medium subdirs)
Format medium subdirectories in packages/features: auth, credentials,
calendars, routing-forms, routing-trace, attributes, watchlist, calAIPhone,
tasker, and webhooks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 3 - bookings + insights)
Format bookings and insights subdirectories in packages/features.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 4 - ee)
Format packages/features/ee subdirectory covering billing, workflows,
organizations, teams, managed-event-types, round-robin, dsync,
integration-attribute-sync, and payments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 5 - booking-audit part 1)
Format booking-audit di, actions, common, dto, repository, and types
subdirectories in packages/features/booking-audit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 6 - booking-audit part 2)
Format booking-audit service subdirectory in packages/features/booking-audit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* perf: use UNION ALL with DISTINCT for bookings query optimization
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add unionAll and distinct to kysely test mocks
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add integration tests for getBookings query
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add totalCount deduplication test for multi-branch union scenario
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: fix totalCount assertion to be exact instead of gte
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Apply suggestion from @keithwillcode
* perf: restore COUNT(DISTINCT id) for totalCount query
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add tests: packages/trpc/server/routers/viewer/bookings/get.handler.integration-test.ts
Generated by Paragon from proposal for PR #27841
* Add tests: packages/trpc/server/routers/viewer/bookings/get.handler.test.ts
Generated by Paragon from proposal for PR #27841
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add no users found fallback action for routing forms
- Add NoUsersFoundFallbackActionType enum with CustomPageMessage and ExternalRedirectUrl options
- Add noUsersFoundFallbackAction field to zodNonRouterRoute schema
- Add NoUsersFoundFallback UI component in RouteBuilder for configuring fallback action
- Update handleResponse to return noUsersFoundFallbackAction when no team members match
- Add i18n translation keys for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: change fallback to use same action options as main route
- Replace noUsersFoundFallbackAction with fallbackAction that has same structure as main action
- Add action dropdown and input fields to fallback attributes query builder section
- Support Event redirect, External URL, and Custom page options in fallback
- Remove separate NoUsersFoundFallback toggle component
- Update handleResponse to return fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure fallbackAction type is always defined in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add guards for route.fallbackAction in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only return fallbackAction when no users are found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: restructure fallback section to mirror main route structure
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure backwards compatibility for existing routes with fallbackAttributesQueryValue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction with main event type for backwards compatibility
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include fallbackAction in getRoutesToSave to persist changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use fallbackAction in getRoutedUrl when no team members found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use i18n for fallback label and add tests for fallback action
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: do not trigger fallback action when CRM contact owner is found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: prioritize fallbackAction over fallbackAttributesQueryValue
When a route has a fallbackAction configured, skip the
fallbackAttributesQueryValue to ensure the fallbackAction is triggered
when no team members are found. This maintains backwards compatibility
by only using fallbackAttributesQueryValue when fallbackAction is not set.
Also treats teamMemberIdsMatchingAttributeLogic being null (routing couldn't
run, e.g., missing orgId) the same as an empty array for the purpose of
triggering the fallbackAction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract RouteActionSelector shared component for action selection UI
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add i18n string for default custom page message
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* revert: remove webhook trigger changes (to be moved to separate PR)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback query builder when fallbackAction is explicitly set
The fallback attributes query builder was being shown for new routes
when fallbackAction was not set, causing an extra 'Add rule' button
to appear. This broke e2e tests that relied on the button index.
Now the fallback query builder is only shown when fallbackAction is
explicitly set to EventTypeRedirectUrl, which happens after the route
is saved and reloaded (via auto-initialization in useRoutes).
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use data-testid for attribute query builder selectors in e2e tests
- Add data-testid='attributes-query-builder' to the main attributes query builder div
- Update addAttributeRoutingRule and addAttributeRoutingRuleWithOperator to use the data-testid selector instead of nth() indices
- This makes the tests more robust and independent of DOM structure changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction for new routes in useCreateRoute hook
- New routes created via useCreateRoute now initialize fallbackAction
- This ensures new routes have a valid fallbackAction when saved
- Matches the initialization logic for existing routes in useRoutes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction at save time when action is EventTypeRedirectUrl
- When saving routes, if fallbackAction is undefined but action is EventTypeRedirectUrl,
initialize fallbackAction from the main action
- This handles the case where user creates a new route and changes action to an event type
- Fixes e2e test failures where form save was failing due to undefined fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction when main action changes to EventTypeRedirectUrl
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback section when main action has valid event type selected
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: check for valid fallbackAction event type before preserving existing value
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Give more structure to main and fallback routes
* Update comment
* Rename fallbackAttributesQueryBuilder to matchingMembersFallbackRoute
* Display custom event redirect input
* fix: address Cubic AI review feedback for routing form fallback
- Simplify getFallbackAction logic to use checkedFallback flag instead of
manually parsing attributesQueryValue (confidence 9/10)
- Add i18n translation for 'And connect with specific team members' string
using t('connect_with_specific_team_members') (confidence 9/10)
Co-Authored-By: unknown <>
* fix: update getFallbackAction to use checkedFallback and handle missing orgId case
- Use checkedFallback flag as primary indicator for triggering fallback action
- Also handle edge case where attribute routing was configured but couldn't run
due to missing orgId (teamMemberIdsMatchingAttributeLogic is null)
- Update test to mock checkedFallback: true when expecting fallback action
Co-Authored-By: unknown <>
* Address UI comment
* fix: Use fallbackAttributesQueryValue when fallback action is event redirect
Only use fallbackAttributesQueryValue when:
1. fallbackAction is EventTypeRedirectUrl (attribute routing applies)
2. fallbackAction is not set (backwards compatibility)
Skip it for CustomPageMessage and ExternalRedirectUrl fallback actions
since attribute routing doesn't apply to those action types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: correct JSX syntax error in matchingMembersFallbackRoute
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract getCustomSlug helper to deduplicate custom event type slug logic
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback attribute filtering for teams under orgs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: show fallback action for all team forms, gate attribute filtering on isOrganization
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
The Date.now() method returns time in epoch miliseconds, adding 60 * 60 = 3.6seconds leads to a bug that results in expiring the access token in 3.6 seconds, although it been valid. It creates unnecessary api calls to the zoho servers before most the api calls.
* feat: add UTM parameter data to insights bookings CSV download
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* test: add UTM data tests and update snapshots for csvDataTransformer
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* fix: use Tracking relation for UTM data instead of flat fields on Booking
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When disableCancelling or disableRescheduling is enabled on an event type,
the confirmation email still showed non-functional cancel/reschedule links.
The ManageLink email component already had correct conditional logic to
hide these links, but two flows constructed the CalendarEvent manually
without including the flags: the booking confirmation handler and the
payment booking flow. This caused the flags to be undefined, which
ManageLink interpreted as enabled.
Added disableCancelling and disableRescheduling to the Prisma select and
CalendarEvent object in both confirm.handler.ts and getBooking.ts.
Closes#22906
* factory and statergie
* chore: use correct method of DI
* feat: add onchagne
* add logic to HWM stat
* add webhook resolver methods to each statergy
* move seat tracking + webhooks over to own statergy
* Move to factory base approach
* move logic to correct class
* rename create -> createByTeamId
* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED
Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.
Identified by cubic (https://cubic.dev)
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize getBusyTimesForLimitChecks with batched parallel queries and raw SQL
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* test: add unit tests for getBusyTimesForLimitChecks batched query optimization
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* refactor: use Prisma findMany instead of raw SQL for better testability
The batching (50 user chunks) and parallel execution (Promise.all) provide
the main performance optimization. Using findMany instead of $queryRaw
maintains the same performance characteristics while being compatible
with prismock for testing.
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: type error
* refactor: add integration test
* chore: remove log
* chore: remove log
* fix: avoid logging PII (userId) in busyTimes debug log
Replace JSON.stringify(busyTimes) with count to avoid exposing userId
in logs. Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* chore: undo
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update booking page terms text to use APP_NAME instead of 'our'
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* remove changes for platform booker
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* inviteMemberUtils in featuers
* update imports
* wip
* wip
* fix: remove PII from log statements in inviteMemberUtils
- Remove email (usernameOrEmail) from error log in sendSignupToOrganizationEmail
- Replace full user object with userId in debug log in sendExistingUserTeamInviteEmails
Co-Authored-By: unknown <>
* revert: restore original code for pure relocation (no formatting/logic changes)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* fix: update test mocks to use new import path for inviteMemberUtils
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert: restore original formatting in proration test and handleGroupEvents
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add DB table for wrong assignment reports
* When report is submitted write to the db
* Prevent duplicate reportings
* test: add migration and tests for WrongAssignmentReport table
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add unique constraint on bookingUid and booking access check for hasWrongAssignmentReport
- Add @unique constraint on bookingUid in WrongAssignmentReport model to prevent duplicate reports at DB level
- Add booking ownership check using BookingAccessService in hasWrongAssignmentReport endpoint
- Refactor hasWrongAssignmentReport into separate handler and schema files
Addresses Cubic AI review feedback on PR #27405
Co-Authored-By: unknown <>
* feat: add routingFormId to WrongAssignmentReport and fix Select clearing
- Add routingFormId field to WrongAssignmentReport model in schema.prisma
- Add relation to App_RoutingForms_Form with SetNull on delete
- Update WrongAssignmentReportRepository.createReport to accept routingFormId
- Update BookingRepository.findByUidIncludeEventTypeAndTeamAndAssignmentReason to include routedFromRoutingFormReponse
- Extract routingFormId from booking in reportWrongAssignment handler
- Fix Select clearing issue: handle null case when user clears team member selection
- Update tests to include routingFormId field
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add migration for routingFormId in WrongAssignmentReport
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address Udit's review comments
- hasWrongAssignmentReport: throw UNAUTHORIZED error instead of returning false
- reportWrongAssignment: add try-catch for Prisma P2002 unique constraint error
- WrongAssignmentReport: add Team relation to teamId field
- WrongAssignmentReportRepository: use findUnique instead of findFirst
- reportWrongAssignment: use i18n for error messages
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: revert hasWrongAssignmentReport to return false when user lacks access
Per PR checklist, hasWrongAssignmentReport should return { hasReport: false }
when user lacks access to booking, not throw an error. This allows the UI
to gracefully treat 'no access' as 'no report exists'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: update mocks for findUnique and i18n in unit tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: throw UNAUTHORIZED in hasWrongAssignmentReport and squash migrations
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: change User relation onDelete from Cascade to SetNull in WrongAssignmentReport
Address Hariom's review feedback:
- Changed reportedById from Int to Int? (nullable)
- Changed reportedBy relation from onDelete: Cascade to onDelete: SetNull
- Updated migration SQL to reflect these changes
This preserves wrong assignment reports even when the reporting user is deleted,
as the data is still useful for analysis.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: hide feature opt-in banner and feedback forms during impersonation
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: keep opt-in banner visible during impersonation, only hide feedback forms
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: allow feedback form during impersonation in development mode
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add activeFilters validator to DataTableProvider for filter segment validation
- Add validateActiveFilters prop to DataTableProvider to filter out inaccessible values
- Create useActiveFiltersValidator hook in bookings module to validate filter values
- Integrate validator in bookings-view.tsx to validate userId, eventTypeId, teamId filters
- Add comprehensive tests for the filter validation logic
This provides a scalable solution for handling stale filter segment data by validating
filters at the caller level using already-fetched accessible resource IDs.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add type guard for eventTypeIds to ensure number[] type
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: delay segment filter application until validator is ready
- Change useActiveFiltersValidator to return 'loading' state while data is fetching
- Add pending segment handling in DataTableProvider to delay filter application
- Expose isValidatorPending in context for consumers to disable queries
- Update BookingListContainer to disable bookings query while validator is pending
This prevents both errors from invalid filters and flash from fetching without filters.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: extract useSegmentManagement hook from DataTableProvider
- Extract segment management logic into dedicated useSegmentManagement hook
- Reduces DataTableProvider from ~500 lines to ~330 lines
- Use NuqsSetter type to match nuqs useQueryState setter signatures
- Maintains all existing functionality and type safety
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: export ActiveFiltersValidator types from data-table lib/types
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: import ActiveFiltersValidatorState from lib/types instead of hook
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Revert "refactor: extract useSegmentManagement hook from DataTableProvider"
This reverts commit dd588ac4cfb1d709c71d1f621994a7e8f3118f33.
* refactor: split DataTableProvider into fine-grained context providers
- Create DataTableStateContext for raw nuqs state management
- Create DataTableSegmentContext for segment management
- Create DataTableFiltersContext for filter manipulation
- DataTableProvider now composes all providers with a bridge component
- Maintains backward compatibility via combined DataTableContext
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: apply segment filters when navigating back to page with existing URL state
When navigating back to a page with a segment already selected in the URL,
the segment was being marked as selected but its filters were not being
applied. This caused non-deterministic behavior where the filter segment
would sometimes not take effect.
The fix ensures that when segmentIdRaw exists in the URL and segments are
fetched, we also call applySegmentFilters() (with validator timing support)
instead of just setting the selectedSegment state.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: simplify filter validator to only handle multi-select
These filters are always multi-select, so remove unnecessary
single-select handling code and tests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: cancel debounced search term on unmount
Cancel the debounced search term on unmount/dependency change to avoid
queued updates firing after the provider unmounts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Users with personal emails (e.g. Gmail) could enter the org creation flow
at /settings/organizations/new but only hit the company email error at
checkout, with no way to fix it from the form. After changing their email
in settings, the cached store email was still used.
- Add server-side isCompanyEmail check in page.tsx to redirect early
- Add client-side check in CreateANewOrganizationForm with clear messaging
- Always use current session email for non-admin users (not cached store)
- Add tests for isCompanyEmail utility
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: add composite index on (formId, createdAt) for App_RoutingForms_FormResponse
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: add migration for formId, createdAt composite index
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Update migration.sql
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: migrate IP-based rate limiting from Unkey to Cloudflare
Remove Unkey rate limiting for IP-based endpoints that will now be
handled by Cloudflare Enterprise Advanced Rate Limiting:
- Regular booking creation (createBooking:{hashedIP})
- Recurring booking creation (createRecurringBooking:{hashedIP})
- Instant meeting creation (instant.event-{hashedIP})
- Booking cancellation for unauthenticated users (api:cancel-ip:{hashedIP})
- Forgot password (forgotPassword:{hashedIP})
- Reset password (api:reset-password:{hashedIP})
- Signup (api:signup:{hashedIP})
- API v1 requests ({userId} with auto-lock)
- Global proxy rate limiting (common namespace)
Rate limiting that remains in Unkey (user/entity-based):
- Login (hashedEmail)
- Booking cancellation for authenticated users (api:cancel-user:{userId})
- 2FA setup/enable/disable (api:totp-*:{userId})
- SMS sending (team/org/user based)
- Email verification (various patterns)
- Team member operations (userId based)
- Routing forms (formId:responseHash)
- AI phone calls (userId based)
Also includes Cloudflare configuration proposal document with
recommended rules using JA4 fingerprinting for enhanced protection.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove cloudflare proposal doc from PR
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove cloudflare comments and keep common rate limiting type
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: revert global middleware from PR #25080 and restore core rate limits
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: restore instantMeeting rate limiting
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: restore email fallback in forgot-password rate limiting
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Remove the static file check
* refactor: add POST_METHODS_ALLOWED_API_ROUTES to proxy matcher
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore API v1 rate limiting to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore reset-password, cancel, book/event, book/recurring-event to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: use POST_METHODS_ALLOWED_API_ROUTES spread in matcher instead of hardcoded routes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore signup route to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: update proxy matcher tests for POST_METHODS_ALLOWED_API_ROUTES spread
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Rename var
* refactor: revert matcher to static strings, add sync-check test for POST_METHODS_ALLOWED_API_ROUTES
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* removed dead routing forms rewrite code
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: remove IS_PRODUCTION gate from BookingAuditProducer
Remove the IS_PRODUCTION check that was preventing booking audits from
being queued in production. Audits are still properly gated by:
1. Organization check: Audits are skipped for non-organization bookings
(organizationId === null)
2. Feature flag: The BookingAuditTaskConsumer checks if the 'booking-audit'
feature is enabled for the organization via featuresRepository.checkIfTeamHasFeature()
The IS_PRODUCTION gate was intentionally added to prevent logs from being
created in production while the action data versioning was being actively
reviewed and finalized. Without proper versioning handling, the Booking
History UI could crash when encountering unversioned data. Now that the
versioning system is in place, this gate can be safely removed.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert formatting, keep only IS_PRODUCTION removal
Reverts the unintended formatting changes from the previous commit.
Only removes the IS_PRODUCTION gate without changing indentation.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add booking-audit feature flag check in producer to avoid unnecessary task creation
- Query booking-audit and booking-email-sms-tasker flags in parallel before fireBookingEvents
- Pass isBookingAuditEnabled through BookingEventHandler to producer's queueTask method
- Add conditional check in queueTask with debug log when skipping audit
- Reuse pre-queried isBookingEmailSmsTaskerEnabled flag instead of querying again
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make isBookingAuditEnabled required and pass it through all booking audit flows
- Make isBookingAuditEnabled a required property in BookingAuditProducerService interface
- Update all BookingEventHandler methods to require isBookingAuditEnabled
- Add feature flag check in all flows that call booking audit:
- handleSeats (seat booking/rescheduling)
- RecurringBookingService (bulk bookings)
- handleCancelBooking (booking cancellation)
- handleConfirmation (booking acceptance)
- roundRobinReassignment (automatic reassignment)
- roundRobinManualReassignment (manual reassignment)
- trpc handlers: addGuests, confirm, editLocation, requestReschedule
- Skip queueing audit tasks when feature is disabled with debug logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add featuresRepository dependency to RecurringBookingService DI module
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: make isBookingAuditEnabled optional for non-main flows
Keep feature flag check only in main flows (handleSeats, RegularBookingService, RecurringBookingService) which are frequently triggered. For other flows (handleCancelBooking, handleConfirmation, roundRobinReassignment, etc.), rely on the existing consumer-level check.
Changes:
- Revert feature flag check from non-main flows
- Make isBookingAuditEnabled optional in interface for non-main flow methods
- Keep isBookingAuditEnabled required for main flow methods (queueCreatedAudit, queueRescheduledAudit, queueSeatBookedAudit, queueSeatRescheduledAudit, queueBulkCreatedAudit, queueBulkRescheduledAudit)
- Update BookingEventHandlerService to use required params for main flows and optional for non-main flows
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: remove isBookingAuditEnabled from non-main flow methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make isBookingAuditEnabled required in all BookingEventHandlerService methods
- Add isBookingAuditEnabled as required parameter in all BookingAuditProducerService interface methods
- Update BookingAuditTaskerProducerService to use simplified check (!params.isBookingAuditEnabled)
- Update BookingEventHandlerService to require isBookingAuditEnabled in all methods
- Update all callers to query booking-audit feature flag and pass isBookingAuditEnabled:
- handleCancelBooking
- handleConfirmation
- roundRobinReassignment
- roundRobinManualReassignment
- addGuests.handler
- confirm.handler
- editLocation.handler
- requestReschedule.handler
- Inject featuresRepository in API V2's booking-location.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: update roundRobinReassignment tests to include isBookingAuditEnabled
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: update roundRobinManualReassignment tests to include isBookingAuditEnabled
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: inject featuresRepository in API V2 RecurringBookingService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: import PrismaWorkerModule for PrismaFeaturesRepository dependency
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use booking's organization for feature flag check in addGuests handler
Use booking.user?.profiles?.[0]?.organizationId instead of user.organizationId
to check the booking-audit feature flag. This ensures the feature flag is
checked against the booking's organization rather than the actor's organization,
which is consistent with other handlers in this PR.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* Add comment
* fix: use user.organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use booking's organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert to user.organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add isBookingAuditEnabled to onNoShowUpdated calls after main merge
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add isBookingAuditEnabled to onNoShowUpdated calls and update tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: prevent 500 error when deleting calendar events with empty uid
This fix addresses a 500 error that occurs when trying to delete a Google
Calendar event with an empty event ID, which results in a malformed API URL.
Root cause: When calendar event creation fails or returns without an ID,
booking references were being created with empty uids. Later, when trying
to delete these events (e.g., during reschedule), the empty uid caused
a 404 from Google Calendar API which was then thrown as a 500 error.
Changes:
1. CalendarManager.deleteEvent: Added validation to skip deletion if
bookingRefUid is empty, with appropriate error logging. This is a
safety net for existing bad data in the database.
2. EventManager.create: Added filtering to exclude booking references
with empty uids from being stored, with error logging to help
diagnose the root cause of missing event IDs.
3. EventManager.updateLocation: Same filtering applied for consistency.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: revert EventManager filtering to preserve existing behavior
The test expects that booking references with empty uids are still created
when calendar event creation fails. This is intentional behavior to track
failed calendar syncs. The CalendarManager.deleteEvent fix handles the
deletion case gracefully by skipping the API call when uid is empty.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: throw ErrorWithCode instead of silently returning when bookingRefUid is empty
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: validate uid before calling calendar.deleteEvent to prevent 500 error
- Add uid check in lastAttendeeDeleteBooking.ts before calling calendar.deleteEvent
- Add uid check in EventManager.updateAllCalendarEvents before calling calendar.deleteEvent
- Revert CalendarManager.deleteEvent changes (validation at call sites is the proper fix)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: add error logging safeguard in CalendarManager.deleteEvent for empty bookingRefUid
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: move bookingRefUid check before getCalendar call
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: prevent storing booking references with empty uid when calendar event creation fails
Root cause fix: Change the check from 'if (createdEvent)' to 'if (createdEvent.createdEvent)'
in createAllCalendarEvents to prevent failed calendar events from being added to results.
This prevents empty uids from being stored in the database in the first place, which
was causing 500 errors when trying to delete non-existent calendar events later.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* test: update test to expect no calendar reference when calendar event creation fails
The root cause fix changes behavior so that failed calendar events no longer
create booking references with empty uids. This test now expects an empty
references array when calendar event creation fails, which is the correct
behavior that prevents 500 errors later when trying to delete non-existent
calendar events.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: use createdEvent.success instead of createdEvent.createdEvent
Using success is more semantically clear since it explicitly checks the
success status rather than checking if the nested object exists.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: use optional chaining for createdEvent?.success
Using optional chaining is safer in case createdEvent is somehow undefined.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert: remove root cause fix to preserve backfilling script compatibility
The root cause fix (checking createdEvent?.success instead of createdEvent)
would break the backfilling script that relies on booking references with
empty uids to identify which bookings need backfilling.
This PR now only includes defensive checks to prevent 500 errors when
deleting calendar events with empty uids.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: move repositories from lib to features domain folders
- Move HolidayRepository to features/holidays/repositories
- Move PrismaTrackingRepository to features/bookings/repositories
- Move PrismaBookingPaymentRepository to features/bookings/repositories
- Move PrismaRoutingFormResponseRepository to features/routing-forms/repositories
- Move PrismaAssignmentReasonRepository to features/assignment-reason/repositories
- Move VerificationTokenRepository to features/auth/repositories
- Move WorkspacePlatformRepository to features/workspace-platform/repositories
- Move DTO files to their respective feature domains
- Merge lib DestinationCalendarRepository into features version
- Merge lib SelectedCalendarRepository into features version
- Update all import paths across the codebase
This follows the vertical slice architecture pattern by organizing
repositories by domain rather than by technical layer.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: update VerificationTokenService import path to new location
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: update test file imports to use new repository locations
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* mv
* fix structure
* fix
* refactor: merge unit tests for SelectedCalendarRepository into single file
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: disable save button when workflow has no changes
* test: wait for save button to be enabled before clicking
* test: ensure input value is set before pressing Enter
* test fix
* fix: add shouldDirty to enable save button on name change
* test: clean up workflow test fixtures
* fix: disable save button when workflow has no changes
* test: update workflow E2E tests
* test: fix workflow E2E
* fix: enable save button when workflow form fields change
* fix tests
* fix: disable save button when workflow has no changes
* remove comments
* chore: improve fixture logic
* cleanup: remove unnecessary changes
* update workflow fixture to handle team workflow creation
---------
Co-authored-by: Pallav <90088723+Pallava-Joshi@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
Co-authored-by: Romit <romitgabani@icloud.com>
Co-authored-by: Romit <romitgabani1.work@gmail.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
* test: add E2E tests for assignAllTeamMembers with attribute segment filters
Cover all 4 attribute types (SINGLE_SELECT, MULTI_SELECT, TEXT, NUMBER)
with booking flow verification plus edge cases (no match, all match).
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use auto-retrying assertion for no-match edge case test
Replace flaky waitForTimeout(2000) + count check with Playwright's
toHaveCount(0, { timeout: 15000 }) which retries until availability
settles, preventing CI timing issues.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add exclusion tests for members without attribute assigned
Add 4 new E2E tests covering the case where team members have no
attribute assigned at all (not just a different value). Each attribute
type (SINGLE_SELECT, MULTI_SELECT, TEXT, NUMBER) is tested to verify
that members without the attribute are excluded from the booking pool.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add negation operator tests and restructure exclusion tests
- Restructure 'Members without attribute assigned' tests: assign
matching attribute to teammate-1 (not owner) so round-robin can't
coincidentally pass when the bug exists
- Add 4 negation operator tests (select_not_any_in, multiselect_not_some_in,
not_equal for TEXT and NUMBER) that verify unassigned members should
match negation filters. Marked with test.fail() as they confirm a
known bug where getLogicResultForAllMembers skips members without
attribute assignments entirely.
- Add edge case: no members have attribute assigned -> no available slots
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: remove test.fail() markers from negation tests after bug fix
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add comprehensive E2E tests for routing form attribute conditions
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: assert routedTeamMemberIds in URL instead of email in results
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: assert both routedTeamMemberIds in URL and email in results
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include users without attribute assignments in exclusion condition evaluation
Users with no attribute assigned were excluded from the team members list
entirely, causing exclusion operators (not_equals, not_some_in, not_like,
not_between, etc.) to never match them. Now getAttributesAssignmentData
includes all team members, even those without attribute assignments.
Also adds unit tests and E2E tests for this scenario.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: update getAttributes test to reflect new behavior for empty attributes
Users without attribute assignments are now included with empty attributes
so exclusion operators can match them. Updated test expectation accordingly.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: remove redundant usersWithAssignments code (fix already in _prepareAssignmentData from main)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: defer email validation to after first blur on signup form
Change react-hook-form validation mode from "onChange" to "onTouched"
so that the "Invalid email" error only appears after the user has
interacted with the field and moved away, not on every keystroke.
Password strength hints still update in real-time since onTouched
validates on change after the field has been touched (blurred once).
Fixes#19163
* test: add unit tests for signup email validation mode
Verify the onTouched form validation behavior:
- No error shown while user is typing
- Error appears only after blur with invalid email
- No error for valid email after blur
- Revalidation on each keystroke after first blur
* remove eslint comment
---------
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Add aria-label to the TimeFormatToggle and LayoutToggle components
on the booking page so screen readers can identify these radio groups.
Uses existing translation keys "time_format" and "layout".
Fixes#20703
- Remove unused attendeeIds field and unnecessary prisma.booking.findUnique from beforeEach
- Store booking.id in testData to avoid re-querying booking in test body
- Track additional attendee emails via additionalAttendeeEmails array for proper afterEach cleanup
- Remove fragile prisma.attendee.delete (throws P2025 on missing record) from test body
- Use deleteMany-based cleanupTestData for second attendee cleanup instead
The test was flaky because:
1. It used prisma.attendee.delete (singular) which throws if record is already gone
2. It re-queried the booking via findUnique mid-test which could fail if cascade-deleted
3. The second attendee email was not tracked in afterEach cleanup
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: prevent textarea resize overlapping buttons in rejection dialog
The rejection reason textarea in the booking rejection dialog allows
manual resizing via the drag handle, which can cause it to expand over
the dialog footer buttons. Adding resize-none prevents this overlap.
Fixes#17536
* Update RejectionReasonDialog.tsx
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
* fix(booking): make provider label and location link visible on mobile
* correct order
* remove comments
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
* fix: resolve organizer default conferencing app credential for location update
- Fix Google Meet credential lookup in EventManager to properly map 'google:meet' to 'google_video' type
- Add automatic credential ID resolution when selecting 'Organizer Default App' as location
- Fixes 'Location update failed' error when editing round-robin team event location to organizer default app (Google Meet) with multiple Google Calendar connections
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix update
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Fix exclusion filter - include all team members
* Fix display when members aren't saved in the DB
* Update tests
* Use integration tests for findTeamMembersMatchingAttributeLogic for better behaviour testing
* Add test that verifies that it was working earlier and broken in b/w and not working again
* Revert changes to AddMembersWithSwitch.tsx
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(ui): change phone booking display from Organizer Phone Number to Phone Call
The label \"Organizer Phone Number\" was confusing for attendees on the
booking page, as it exposed internal terminology. Replaced with the
more user-friendly \"Phone Call\" label across the booking UI, companion
app, and test assertions.
Closes#13010
* fix: remove duplicate phone_call i18n key
The phone_call key already existed in en/common.json (line 1685,
Cal.ai Voice Agent section). Removed the duplicate entry we added.
Found by ai-codex review.
* fix: use LOGO constant for generic OG image instead of hardcoded value
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* test: add unit tests for OgImages module
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: remove unused LOGO import from OgImages test
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* test: update OgImages tests to reflect LOGO_DARK constant
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* add comment
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Revert "Revert "feat: redesign team creation flow to match onboarding-v3 design (#26733)""
This re-applies the team creation redesign from PR #26733 which was
previously reverted. Cherry-picked from 2540423ba3.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: check org/username slug when in org context
* fix: use expect(page).toHaveURL() instead of page.waitForURL() in team e2e test
Co-Authored-By: unknown <>
* fix: redirect URL
* fix: flag import
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add upcoming bookings widget for iOS and Android
- Add iOS widget using @bacons/apple-targets with SwiftUI
- Add Android widget using react-native-android-widget
- Configure App Groups for iOS data sharing between app and widget
- Create widget sync hook to update widget data when bookings change
- Widget displays up to 5 upcoming bookings with title, time, and attendee
- Widget refreshes every 15 minutes and when app goes to background
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): refactor useWidgetSync to avoid React Compiler limitation
Move conditional logic outside of try/catch block to fix React Compiler
error: 'Support value blocks (conditional, logical, optional chaining,
etc) within a try/catch statement'
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): fix widget data sync issues
- Fix Swift widget to read data as JSON string (format used by react-native-shared-group-preferences)
- Add fallback to read as raw Data for compatibility
- Fix query key mismatch by checking multiple common filter combinations
- Ensure widget finds cached bookings regardless of which filter the app used
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): use ExtensionStorage from @bacons/apple-targets for widget data
- Replace react-native-shared-group-preferences with ExtensionStorage
- ExtensionStorage properly stores data in iOS App Groups
- Add ExtensionStorage.reloadWidget() call to trigger widget refresh
- This ensures the widget picks up new data immediately
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat(companion): add upcoming bookings widget for ios - all shapes (#27564)
* ios working widget
* fix(companion): remove sensitive data logging from widget storage
Remove console.log statements that were logging full widgetData payload
and formatted widget bookings containing attendee names and booking
details. This addresses security concerns identified by Cubic AI review
(confidence 9/10).
Co-Authored-By: unknown <>
* dark mode
* new look
* fix deeplink
* fix biome lint
* fix(companion): remove booking titles from console logs to avoid PII exposure
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* lock the versions and dev mode logging
* remove dynamic imports
* feat(companion): upcoming bookings widget android (#27689)
* companion-upcoming-bookings-widget-android
* fix(companion): remove stale relative countdowns and fix past-time clamping in Android widget
- Remove relative countdown text (In 15m, In 1h, Now) that becomes stale
due to Android widget 30+ min update interval (Cubic violation 2)
- Remove Math.max(0, minutes) clamping that caused ended meetings to
display as 'Now' indefinitely (Cubic violation 1)
- Simplify accent color logic to use startTimeISO directly
- Keep absolute time display (date + startTime) which remains accurate
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
* fix: event not created
* Update CalendarManager.ts
* Add tests: packages/features/calendars/lib/CalendarManager.ts
Generated by Paragon from proposal for PR #27675
* Add tests: packages/features/calendars/lib/CalendarManager.test.ts
Generated by Paragon from proposal for PR #27675
* Export processEvent for testing and refactor logic
* Fix missing newline at end of CalendarManager.test.ts
Add missing newline at the end of the file.
* fix test
* refactor: simplify link-as-an-app template to config.json only
- Remove api/, components/, index.ts, package.json from link-as-an-app template
- Add externalLink field to AppMetaSchema for external URL configuration
- Update API handler to dynamically create handlers for external link apps
- External link apps no longer create credentials (just redirect to URL)
- Update CLI to handle externalLinkUrl field for link-as-an-app template
- CLI now skips package.json update for apps without package.json
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: simplify external link handler to bypass credential creation
External link apps now directly return the redirect URL without going
through the AppDeclarativeHandler type, avoiding type conflicts with
the createCredential return type requirement.
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: migrate 24 external link apps to simplified structure
Migrated the following apps to use config.json with externalLink field
instead of api/add.ts handlers:
- amie, autocheckin, baa-for-hipaa, bolna, chatbase, clic, cron, deel
- elevenlabs, fonio-ai, framer, granola, greetmate-ai, lindy, millis-ai
- monobot, n8n, pipedream, raycast, retell-ai, synthflow, telli, vimcal
- wordpress
Each app now only contains: config.json, DESCRIPTION.md, static/
Removed: api/, components/, index.ts, package.json
Co-Authored-By: peer@cal.com <peer@cal.com>
* nit
* Fix merge conflict issue
* refactor: generate REDIRECT_APPS dynamically during app-store build
- Add logic to build.ts to detect apps with externalLink field
- Generate redirect-apps.generated.ts with list of redirect app slugs
- Update redirectApps.ts to import from generated file
- Revert [...]args].ts to original state (Flow 1 is preferred)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: cast REDIRECT_APPS to readonly string[] for includes check
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix error with React
* Take exteral link URL as input
* deleted bun.lockb
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* chore: Integrate mark-no-show booking audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Simplify host no-show audit and add comment for attendee actor
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Make actionSource required with ValidActionSource type and remove unnecessary comments
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove merge conflict markers from bookings.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: implement SYSTEM as a booking audit source for background tasks, including no-show triggers.
* refactor: Move prisma query to BookingRepository for audit logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Conflict resolution
* refactor: introduce `handleMarkHostNoShow` for public viewer and standardize actor and action source parameters for no-show actions.
* refactor: Combine HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService into NoShowUpdatedActionService
- Create new NoShowUpdatedAuditActionService with combined schema supporting both noShowHost and noShowAttendee as optional fields
- Update BookingAuditActionServiceRegistry to use combined service with NO_SHOW_UPDATED action type
- Update BookingAuditTaskerProducerService with single queueNoShowUpdatedAudit method
- Update BookingAuditProducerService.interface.ts with combined method
- Update BookingEventHandlerService with single onNoShowUpdated method
- Update handleMarkNoShow.ts to use combined audit service
- Update tasker tasks (triggerGuestNoShow, triggerHostNoShow) to use combined service
- Add NO_SHOW_UPDATED to Prisma BookingAuditAction enum
- Remove old separate HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService files
This allows a single API action (e.g., API V2 markAbsent) that updates both host and attendee no-show status to be logged as a single audit event.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: Add migration for NO_SHOW_UPDATED audit action enum value
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update no-show audit schema to use host and attendees array
- Rename noShowHost to host and noShowAttendee to attendees (remove redundant prefix)
- Change attendees from single value to array to support multiple attendees in single audit entry
- Update handleMarkNoShow.ts to create single audit entry for all attendees
- Update tasker tasks (triggerGuestNoShow, triggerHostNoShow) to use new schema
- Update display data types to reflect new schema structure
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Update schema to be more clear. Call onNoShowUpdated immediately after DB update as audit only cares about DB update, and this would avoid any accidental Audit update on DB change
* chore: accommodate schema changes and fix type errors for no-show audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update migration to remove old no-show enum values
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use updated attendees in webhook payload for guest no-show
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove duplicate imports and fix actor parameter in bookings.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Move booking query to BookingRepository and remove unused method
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: centralize no-show audit event firing and attendee fetching within `handleMarkNoShow` for improved clarity.
* fix: Build attendeesNoShow as Record with attendee IDs as keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix and add tests for handleMarkBoShow
* refactor: Move prisma queries to AttendeeRepository and BookingRepository
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Correct return type for updateNoShow method (noShow can be null)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update handleMarkNoShow tests to mock new repository methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix triggerGuestNoShow
* refactor: Move triggerGuestNoShow queries to AttendeeRepository and add unit tests
- Add new methods to AttendeeRepository:
- findByBookingId: Get attendees with id, email, noShow
- findByBookingIdWithDetails: Get full attendee details
- updateManyNoShowByBookingIdAndEmails: Update specific attendees
- updateManyNoShowByBookingIdExcludingEmails: Update all except specific emails
- Refactor triggerGuestNoShow.ts to use AttendeeRepository instead of direct Prisma calls
- Add comprehensive unit tests for triggerGuestNoShow following handleMarkNoShow.test.ts pattern:
- Mock repositories and external services
- In-memory DB simulation
- Test core functionality, audit logging, webhook payload, error handling, and edge cases
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Move triggerHostNoShow queries to repositories and delete unit test file
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Convert repository methods to use named parameters objects
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Enhance no-show handling by consolidating audit logging and updating repository interactions
- Refactor `buildResultPayload` to accept a structured argument for better clarity.
- Update `updateAttendees` to use a named parameter object for improved readability.
- Introduce `fireNoShowUpdatedEvent` to centralize no-show audit logging for both hosts and guests.
- Remove redundant methods and streamline attendee retrieval in `AttendeeRepository`.
- Add comprehensive unit tests for no-show event handling, ensuring accurate audit logging and webhook triggering.
* test: Add tests for handleMarkHostNoShow guest actor and unmarking host no-show
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: update attendeesNoShow validation to handle numeric keys
- Changed attendeesNoShow schema to use z.coerce.number() for key validation, ensuring string keys are correctly coerced to numbers.
- Updated test to validate attendeesNoShow data using numeric key comparison, improving robustness of the audit data checks.
* test: Add integration tests for NoShowUpdatedAuditActionService coerce fix
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add graceful error handling for audit log enrichment
- Add hasError field to EnrichedAuditLog type
- Create buildFallbackAuditLog() method for failed enrichments
- Wrap enrichAuditLog() in try-catch to handle errors gracefully
- Add booking_audit_action.error_processing translation key
- Update BookingHistory.tsx to show warning icon for error logs
- Hide 'Show details' button for logs with hasError
- Add comprehensive test cases for error handling scenarios
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Enhance booking audit action services with new display fields and improved validation
- Added "attendee_no_show_updated" and "no_show_updated" translations to common.json.
- Updated IAuditActionService to include new methods for handling display fields and migration.
- Enhanced NoShowUpdatedAuditActionService to differentiate between host and attendee no-show updates.
- Improved ReassignmentAuditActionService to ensure consistent handling of audit data.
- Refactored BookingAuditViewerService for better clarity and maintainability.
* refactor: Use attendeeEmail instead of attendeeId as key in audit data and store host's userUuid
- Updated schema to use attendee email as key instead of attendee ID because attendee records can be reused with different person's data
- Store host's userUuid in audit data with format: host: { userUuid, noShow: { old, new } }
- Updated fireNoShowUpdated to accept hostUserUuid parameter
- Added uuid field to BookingRepository.findByUidIncludeEventTypeAttendeesAndUser
- Updated NoShowUpdatedAuditActionService getDisplayFields to work with email-based keys
- Added attendeeRepository to DI modules for BookingAuditTaskConsumer
- Updated tests to match new schema format
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Update integration tests to use new schema format with host.userUuid and email keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: correct audit schema for SYSTEM source and ensure attendee lookup by bookingId
- Fix schema inconsistency: use host.userUuid format instead of hostNoShow
- Add user.uuid to getBooking select for audit logging
- Log warning when hostUserUuid is undefined but host no-show is being updated
- Use email as key for attendeesNoShow (not attendee ID) to match schema
- Fetch attendees by bookingId before updating to ensure correct scoping
- Remove unused safeStringify import
* refactor: Change attendeesNoShow schema from Record to Array format
- Update NoShowUpdatedAuditActionService schema to use array format:
attendeesNoShow: Array<{attendeeEmail: string, noShow: {old, new}}>
- Update handleMarkNoShow.ts to build attendeesNoShow as array
- Update common.ts fireNoShowUpdatedEvent to convert Map to array
- Update all tests to use new array format with attendeeEmail property
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update attendeesNoShow schema to use array format in triggerNoShow tasks
- Refactor `triggerGuestNoShow` and `triggerHostNoShow` to replace Map with array for `attendeesNoShowAudit`.
- Update `fireNoShowUpdatedEvent` to handle the new array format for attendees.
- Ensure consistent data structure across no-show audit logging for better clarity and maintainability.
* fix: Update ReassignmentAuditActionService to use GetDisplayFieldsParams interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update ReassignmentAuditActionService tests to use GetDisplayFieldsParams interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use handleMarkAttendeeNoShow for API v2 mark absent endpoint
- Export handleMarkAttendeeNoShow from platform-libraries
- Update API v2 bookings service to use handleMarkAttendeeNoShow with actionSource
- Add validation for userUuid before calling handleMarkAttendeeNoShow
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Enhance display fields structure in booking audit components
- Updated `BookingHistory.tsx` to allow for more flexible display fields, including optional raw values and arrays of values.
- Introduced `DisplayFieldValue` component for rendering display fields, improving clarity and maintainability.
- Adjusted `IAuditActionService` and `NoShowUpdatedAuditActionService` to align with the new display fields structure.
- Ensured consistent handling of display fields across the booking audit service for better data representation.
* fix: Remove debug code that duplicated first attendee in display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update attendee repository methods to use structured parameters
- Modified `updateNoShow`, `updateManyNoShowByBookingIdAndEmails`, and `updateManyNoShowByBookingIdExcludingEmails` methods to accept structured `where` and `data` parameters for better clarity and maintainability.
- Updated calls to these methods throughout the codebase to reflect the new parameter structure.
- Removed unused `findByIdWithNoShow` method and adjusted related logic to streamline attendee data retrieval.
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update test mock to match new AttendeeRepository.updateNoShow signature
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: rename handleMarkAttendeeNoShow to handleMarkNoShow and update related types
- Renamed `handleMarkAttendeeNoShow` to `handleMarkNoShow` for clarity.
- Updated type definitions to reflect the new naming convention.
- Modified the `markNoShow` handler to require `userUuid` and adjusted related logic.
- Enhanced the `fireNoShowUpdatedEvent` to accommodate changes in event type structure.
- Updated references across various files to ensure consistency with the new function name.
* handle null value
* fix: add explicit parentheses for operator precedence clarity
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix cubic reported bug
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* removed previious pr ommit from thi sone
* fixed addressed issues
* fallback to non-empty label for variant fields
* fallback to default label
* normalize empty labels and fallback to default when label equals field name
* fix: initially use empty label for name field (fullName variant)
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: remove normalization of empty field labels in FormBuilder
* fix: enhance label handling in getTranslatedConfigVariants function
* Update apps/web/modules/event-types/components/tabs/advanced/FormBuilder.tsx
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Pallav <90088723+Pallava-Joshi@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Integrate creation/rescheduling booking audit
* fix: add missing hostUserUuid to booking audit test data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix-ci
* feat: enhance booking audit with seat reference
- Added support for seat reference in booking audit actions.
- Updated localization for booking creation to include seat information.
- Modified relevant services to pass attendee seat ID during booking creation.
* fix: update test data to match schema requirements
- Add seatReferenceUid: null to default mock audit log data
- Add seatReferenceUid: null to multiple audit logs test case
- Convert SEAT_RESCHEDULED test data to use numeric timestamps instead of ISO strings
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Allow nullish seatReferenceUid
* feat: enhance booking audit to support rescheduledBy information
- Updated booking audit actions to include rescheduledBy details, allowing tracking of who rescheduled a booking.
- Refactored related services to accommodate the new rescheduledBy parameter in booking events.
- Adjusted type definitions and function signatures to reflect the changes in the booking audit context.
* Avoid possible run time issue
* Fix imoport path
* fix failing test due to merge from main\
* Pass useruuid
* Refactor getDisplayTitle method signatures to use _params for consistency across audit action services. Update IAuditActionService to include dbStore in GetDisplayTitleParams for improved data handling during title retrieval.
* fix: extend bulk-fetching optimization to actor and impersonator enrichment
- Collect actor userUuids (for USER type actors) in collectDataRequirements
- Collect impersonator UUIDs from log.context.impersonatedBy
- Update enrichActorInformation to use dbStore.getUserByUuid() for USER actors
- Update enrichImpersonator to use dbStore.getUserByUuid()
- Update tests to verify findByUuids is called with correct aggregated UUIDs
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Remove dbStore usage from audit action services after merge
- Update CreatedAuditActionService to use userRepository.findByUuid directly
- Remove unused dbStore parameter from RescheduledAuditActionService
- Sync test file with prepare-for-no-show-audit branch
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Restore dbStore bulk-fetching optimization pattern
- Add dbStore parameter and getDataRequirements method to IAuditActionService interface
- Update CreatedAuditActionService to use dbStore.getUserByUuid() for bulk-fetched data
- Update RescheduledAuditActionService to accept dbStore parameter
- Update BookingAuditViewerService to:
- Collect data requirements from all audit logs
- Bulk-fetch users with findByUuids() before enrichment
- Pass dbStore to action services and enrichment methods
- Update tests to verify findByUuids is called for bulk-fetching optimization
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove duplicate fireBookingEvents method in RecurringBookingService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* revert: Remove formatting-only changes in audit action services
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Make getDataRequirements required and fetch rescheduled logs early
- Make getDataRequirements a required method in IAuditActionService interface
- Add getDataRequirements to all action services that didn't have it
- Fetch rescheduled logs early to include their data requirements in bulk fetch
- Update ReassignmentAuditActionService to use dbStore pattern instead of repository
- Update NoShowUpdatedAuditActionService to use dbStore pattern
- Update tests to use new dbStore pattern
- Handle invalid data gracefully in collectDataRequirements
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Remve unncessary variable
* feat: Implement Actor Strategy pattern for bulk-fetching attendees and credentials
- Add ActorStrategies.ts with strategy pattern for all 5 actor types
- Extend EnrichmentDataStore with StoredAttendee and StoredCredential types
- Add findByIds method to CredentialRepository for bulk-fetching
- Update BookingAuditViewerService to use strategy pattern:
- Replace switch statement in enrichActorInformation with strategy dispatch
- Update collectDataRequirements to collect attendeeIds and credentialIds
- Update buildEnrichmentDataStore to bulk-fetch in parallel
- Update tests to use findByIds mock and reflect graceful handling of missing data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Restore error-throwing for actors with missing required IDs
- USER actor now throws error when userUuid is missing
- ATTENDEE actor now throws error when attendeeId is missing
- Updated tests to expect hasError: true for fallback logs
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Restore original test assertions, only update mocks for bulk methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Add contract verification tests for getDataRequirements
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Clean up imports and improve type definitions in BookingAuditTaskConsumer and BookingAuditViewerService
- Consolidated import statements for better readability
- Enhanced type definitions for clarity and consistency
- Updated method signatures to use destructured parameters for improved maintainability
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Split contract verification tests into separate files per action service
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Rename contract test files to .test.ts and merge into existing test file
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add runtime validation to EnrichmentDataStore for undeclared data access
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Make declaredRequirements required and add ensureFetched helper
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Simplify EnrichmentDataStore with constructor + fetch() pattern
- Remove unused userIds and bookingUids from DataRequirements
- Remove unused getUserById and getBookingByUid methods
- Refactor constructor to accept requirements and repositories
- Add fetch() method that fetches data and populates maps
- Use Map keys as source of truth for declared requirements
- Update getters to throw when accessing undeclared data
- Add comprehensive unit tests for EnrichmentDataStore
- Update BookingAuditViewerService to use new API
- Update contractVerification.ts to remove unused tracking
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Clean up imports and improve type definitions in BookingAuditTaskConsumer and BookingAuditViewerService
- Consolidated import statements for better readability
- Enhanced type definitions for clarity and consistency
- Updated method signatures to use destructured parameters for improved maintainability
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Include contextRequirements in mergeDataRequirements and add accessedData assertions
- Fix bug where contextRequirements (impersonator UUIDs) were not merged
- Add accessedData assertions to CreatedAuditActionService.test.ts
- Add accessedData assertions to ReassignmentAuditActionService.test.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: replace FeaturesRepository with DI-based feature repositories
Migrate from the legacy FeaturesRepository pattern to separate DI-based repositories:
- Add checkIfFeatureIsEnabledGlobally to IFeatureRepository
- Add getTeamsWithFeatureEnabled to ITeamFeatureRepository
- Update CalendarSubscriptionService to use featureRepository, teamFeatureRepository, and userFeatureRepository
- Update SelectedCalendarRepository.findNextSubscriptionBatch to filter by teamIds instead of featureIds
- Update OnboardingPathService to use DI via getFeatureRepository()
- Remove prisma parameter from OnboardingPathService callsites
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: update tests to use new DI-based feature repositories
Update CalendarSubscriptionService and SelectedCalendarRepository tests
to use the new separate repository interfaces:
- featureRepository for global feature checks
- teamFeatureRepository for team-level feature checks
- userFeatureRepository for user-level feature checks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update API routes to use DI-based feature repositories
Update cron and webhook routes to use the new separate repository
interfaces instead of the combined FeaturesRepository.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove prisma arg from getGettingStartedPathWithParams call
The OnboardingPathService method no longer requires a prisma argument
as it now uses DI containers internally.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: update route tests to expect new DI-based feature repositories
Update service instantiation tests to expect featureRepository,
teamFeatureRepository, and userFeatureRepository instead of the
old featuresRepository.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: optimize global feature check and add guard in checkForNewSubscriptions
- Use targeted select for only `enabled` field in checkIfFeatureIsEnabledGlobally
- Add global feature flag guard in checkForNewSubscriptions to avoid unnecessary
DB queries and API calls when the cache feature is globally disabled
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* remove redundant comment
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Validates webhook URLs on create and update:
- HTTPS required (HTTP allowed for self-hosted and E2E)
- Blocks private IP ranges and localhost
- Blocks cloud metadata endpoints
Existing webhooks are preserved: validation only applies when URL is created or changed.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: ux audit changes
* fix: remove locale-insensitive toLowerCase() on translation
Remove .toLowerCase() call on t('minutes') translation as it is
locale-insensitive and can corrupt translations (e.g., Turkish
dotted/dotless 'i'). The translation itself should provide the
desired casing.
Addresses Cubic AI review feedback with confidence 9/10.
Co-Authored-By: unknown <>
* fix: redirect url tool tip not working
* fix: type check
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize team page data fetching by reducing host user data
For team view pages (/team/[slug]), event type hosts only need minimal
user data for avatar display (id, name, username, avatarUrl).
Previously, the full userSelect was used which included:
- teams (with nested team data)
- credentials (with app and destinationCalendars)
- email, bio
This optimization reduces data transfer significantly for teams with
many event types and hosts. With 441K requests and 54GB outgoing data
(~128KB per request), even small reductions per request add up.
The full userSelect is still used when !isTeamView to support the
connectedApps feature.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize team page serialization to reduce data transfer
- Replace spread operators with explicit field selection for eventTypes
- Only send minimal user data needed for UserAvatarGroup: name, username, avatarUrl, profile
- Override eventTypes in return props with minimalEventTypes
- Reduces per-request data transfer by excluding unnecessary fields like email, bio, teams, credentials
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add missing fields needed by EventTypeDescription component
Add metadata, seatsPerTimeSlot, requiresConfirmation to minimalEventTypes
Add id to user object for proper key handling
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: remove serialization changes that broke TypeScript types
Keep only the query-level optimization in queries.ts which reduces
database load by fetching minimal user data for event type hosts
when isTeamView=true.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: reduce team page client payload with explicit DTO types
- Create TeamPage.types.ts with explicit DTO types for minimal data
- Update getServerSideProps.tsx to return only fields needed by the UI
- Update team-view.tsx to use explicit types instead of inferSSRProps
This reduces the data sent to the client by:
- Event types: only id, title, slug, description, length, schedulingType,
recurringEvent, metadata, requiresConfirmation, seatsPerTimeSlot
- Event type users: only id, name, username, avatarUrl, avatar, profile
- Members: only fields needed by Team component
- Children: only slug and name
- Parent: only id, slug, name, isOrganization, isPrivate, logoUrl
Combined with the query-level optimization, this should significantly
reduce the ~128KB average payload per request.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Revert: explicit DTO approach due to type compatibility issues
The explicit DTO types broke compatibility with existing components
(EventTypeDescription, UserAvatarGroup) which expect specific type
structures. Keeping only the query-level optimization.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: reduce team page client payload with minimal data serialization
- Update EventTypeDescription to accept minimal type (only fields actually used)
- Update UserAvatarGroup to accept minimal user type
- Update UserAvatar to accept minimal profile type
- Update Team component to accept minimal member type
- Update getServerSideProps to explicitly select only needed fields
This reduces the ~128KB client payload by removing unused fields from:
- Event types: removed hidden, price, currency, lockTimeZoneToggleOnBookingPage, etc.
- Event type users: only send name, username, avatarUrl, avatar, profile.username, profile.organization.slug
- Team members: only send fields needed by Team component
- Team parent/children: only send fields needed for display
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: revert component type changes, keep serialization optimization
- Revert EventTypeDescription, UserAvatarGroup, UserAvatar, Team component types to original
- Keep serialization optimization in getServerSideProps.tsx
- Add missing fields (price, currency, hidden, etc.) for EventTypeDescription compatibility
- Add full profile structure for UserAvatarGroup compatibility
This reduces client payload by explicitly selecting only needed fields while
maintaining type compatibility with existing component usages.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add missing fields for component type compatibility
- Add lockedTimeZone and canSendCalVideoTranscriptionEmails for EventTypeDescription
- Add organizationId to members for Team component MemberType
- Add name, calVideoLogo, bannerUrl to organization for UserProfile type
- Reorder fields to match baseEventTypeSelect structure
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The error 'TypeError: value.hasOwnProperty is not a function' can occur
when objects are created without the standard Object prototype (e.g., via
Object.create(null) or certain parsing methods). Using
Object.prototype.hasOwnProperty.call() is the safe way to check for
property existence on any object.
Fixed in:
- GetSlotsInputPipe (isById, isByUsernameAndEventTypeSlug, isByTeamSlugAndEventTypeSlug)
- CancelBookingInputPipe (isCancelSeatedBookingInput)
- CreateBookingInputPipe (isRecurringBookingInput, isInstantBookingInput)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add delayed formbricks tracking for feature opt-in
Adds delayed Formbricks survey tracking for feature opt-in. When a user
opts into a feature, this allows triggering a Formbricks action after a
configurable delay (e.g., 24 hours later) to collect feedback once
they've had time to use the feature.
Key changes:
- Added `formbricks` config option to `OptInFeatureConfig` interface
with `actionName` and `delayMs` properties
- Created `useFormbricksOptInTracking` hook that handles the delayed
tracking logic
- Added `isFeatureTracked` / `setFeatureTracked` storage helpers to
prevent duplicate tracking
- Integrated the tracking hook into `useFeatureOptInBanner`
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* upgrade formbricks
* feat: replace formbricks popup with custom feedback dialog
Instead of using Formbricks' built-in popup, we now show a custom
Cal.com-styled feedback dialog that submits responses directly to
Formbricks API via tRPC mutation.
- Add FeedbackDialog component with emoji rating selector
- Add feedback tRPC router for server-side Formbricks submission
- Update useFormbricksOptInTracking to return dialog state
- Add survey config fields (surveyId, questions) to config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: position feedback dialog at bottom-right corner
- Use base-ui Dialog primitives for custom positioning
- Position dialog at bottom-right to avoid Intercom overlap
- Use z-index 10000 (below Intercom's high z-index)
- Keep blocking backdrop for modal behavior
- Use i18n keys for title/description
- Add survey IDs for bookings-v3 feedback
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add i18n keys for feedback dialog title/description
Allow each feature to specify custom i18n keys for the feedback dialog
title and description via the formbricks config.
- Add titleKey/descriptionKey to formbricks config interface
- Pass i18n keys through feedbackDialogProps
- Add bookings_v3_feedback_title/description translation keys
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: move FeedbackDialog into FeatureOptInBannerWrapper
Better encapsulation - consumers of the feature opt-in banner
no longer need to handle the feedback dialog separately.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add 5 second delay before showing feedback dialog
Ensures the page has time to finish loading before showing
the feedback dialog, avoiding showing it while skeletons
are still visible.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: simplify feedback dialog UI
- Remove redundant question labels
- Add "(optional)" to comment placeholder
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove emoji button borders and add footer gap
- Remove borders from rating emoji buttons
- Add proper gap between textarea and footer (pb-4)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: delayMs is opt-in waiting period, not setTimeout delay
delayMs represents the minimum time that must pass since opt-in
before showing the feedback form (e.g., 3 days). If not enough
time has passed, we skip showing the form entirely instead of
setting a long setTimeout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: custom feedback dialog for feature opt-in
- Replace Formbricks popup with Cal.com-styled dialog
- Add configurable delay (waitAfterDays) before showing feedback
- Position dialog at bottom-right, non-blocking
- Add localStorage tracking to prevent duplicate feedback
- Add device targeting (showOn: desktop/mobile/all)
- Create tRPC endpoint for Formbricks API submission
- Use proper logger for error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: rename tracking terminology to feedback
- Rename useFormbricksOptInTracking → useOptInFeedback
- Rename FormbricksOptInTrackingResult → OptInFeedbackState
- Rename formbricksTracking property → feedback
- Rename FormbricksTrackingState → FeedbackState
We no longer "track" events to Formbricks. Instead, we show
our custom feedback dialog when conditions are met.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: set waitAfterDays to 3 for production feedback delay
Co-Authored-By: unknown <>
* fix: update formbricks JS SDK usage for v3.0.0
The @formbricks/js SDK v3.0.0 changed its API:
- setup() no longer accepts debug, userId, or attributes
- Use setUserId() and setAttributes() after setup instead
- track() now expects { hiddenFields: ... } or undefined
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* add owner email to admin oauth view
* fix type error
* fix oauth flow with sign up
* improve e2e tests
* fix: use dynamic user email in OAuth client e2e test
The test 'editing redirect uri of an approved client triggers reapproval'
creates a new user and assigns the OAuth client to that user, but was
incorrectly expecting 'admin@example.com' as the owner email. Changed to
use user.email to correctly validate the owner email matches the created
user.
Addresses Cubic AI review feedback (confidence: 9/10)
Co-Authored-By: unknown <>
---------
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: apply biome formatting to Phase 1 packages
Packages formatted:
- packages/app-store-cli
- packages/config
- packages/dayjs
- packages/debugging
- packages/embeds/embed-react
- packages/embeds/embed-snippet
- packages/kysely
- packages/platform/constants
- packages/platform/utils
- packages/testing
- packages/tsconfig
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: restore non-null assertions in embed-snippet to fix type error
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: remove ESM import from CommonJS config file
Biome incorrectly added 'import process from node:process' to a CommonJS
file that uses require(). This breaks Jest which expects CommonJS syntax.
The process global is already available in Node.js without explicit import.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: exclude packages/embeds from Phase 1 formatting
Per user request, reverting all biome formatting changes in packages/embeds
to handle separately.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: restore const enum for BookingLocations
Biome changed 'const enum' to 'enum' which is a semantic change.
Reverting to keep the PR purely formatting-only.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: refactor UI to use coss + coupons UI
* fix: use TRPCError and remove sensitive logging in createCoupon handler
- Replace plain Error with TRPCError for consistent tRPC error handling
- Use INTERNAL_SERVER_ERROR for missing env configuration
- Use UNAUTHORIZED for permission denied errors
- Use BAD_REQUEST for API failure errors
- Remove console.warn that logged username (sensitive info)
Co-Authored-By: unknown <>
* feat: add copy button and fix 0 on % off
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* wip
* fix: claue reveiew commentsd
* add seed test
* fix: seed test plus subscriptin undefined in webhook
* chore: udpate triggr
* Integration test + HWM seed script
* add feature flag for hmw
* Remove redudant docs
* feat: add hwm-seating feature flag for high water mark billing
- Add new hwm-seating feature flag to control HWM billing logic
- Update HighWaterMarkService to use hwm-seating flag
- Update SeatChangeTrackingService to use hwm-seating flag
- Update BillingPeriodService to use hwm-seating flag
- Add flag to migration SQL
- Enable flag in seed script and integration test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add hwm-seating to useFlags and fix test mocks
- Add hwm-seating to useFlags.ts initial data
- Add shouldApplyHighWaterMark mock to TeamBillingService tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix test
* feat: move to invoice paid subscription
* fix: transactions for transiants updates + reusable util
* fix: address Cubic AI review feedback for HWM billing
- Fix dotenv import hoisting in seed-hwm-test.ts by using side-effect import
- Remove logging of sensitive information (password/emails) in seed script
- Fix incorrect newHighWaterMark return value when update is not performed
Co-Authored-By: unknown <>
* add additional unit tests
* improve DI
* fix migration + restore package.json trigger update
* feat:move to featurerepo+update tests
* fix mocks
* fix DI calls
* revert faeturerepo to featuresrepository
* fix: pass featureRepository to BillingPeriodService and HighWaterMarkService
- Fix TeamBillingService.ts: pass featureRepository to BillingPeriodService constructor
- Fix _invoice.upcoming.ts: pass featureRepository to HighWaterMarkService constructor
- Fix hwm-webhook-utils.ts: pass featureRepository to HighWaterMarkService constructor
These services require featureRepository for feature flag checks but production
call sites were missing this required dependency, causing runtime errors.
Addresses Cubic AI review feedback on PR #27559
Co-Authored-By: unknown <>
* fix import
* update feature repo usage to features
* remove test thats mocking the wrong stuff
* revert RegularBookingService changes
* remove hide branding
* remove change in redudant file
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add delete confirmation dialog to webhook list items
* adds appname for translation key used in alert
* extract delete wehook dialog into separate component
* add e2e test
* chore update e2e locator
---------
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
Co-authored-by: Romit <romitgabani@icloud.com>
If your Node version does not meet the project's requirements as instructed by the docs, "nvm" (Node Version Manager) allows using Node at the version required by the project:
@@ -671,7 +678,7 @@ Certain versions may have trouble creating a user if the field `metadata` is emp
##### CLIENT_FETCH_ERROR
If you experience this error, it may be the way the default Auth callback in the server is using the WEBAPP_URL as a base url. The container does not necessarily have access to the same DNS as your local machine, and therefor needs to be configured to resolve to itself. You may be able to correct this by configuring `NEXTAUTH_URL=http://localhost:3000/api/auth`, to help the backend loop back to itself.
If you experience this error, it may be the way the default Auth callback in the server is using the WEBAPP_URL as a base url. The container does not necessarily have access to the same DNS as your local machine, and therefore needs to be configured to resolve to itself. You may be able to correct this by configuring `NEXTAUTH_URL=http://localhost:3000/api/auth`, to help the backend loop back to itself.
Circular dependencies between packages cause build failures, type errors, and make the codebase unmaintainable. The dependency graph must be acyclic, with clear layers from low-level utilities up to high-level features.
The dependency hierarchy (from lowest to highest):
```
packages/lib (lowest - no feature dependencies)
↓
packages/app-store
↓
packages/features
↓
packages/trpc
↓
apps/web (highest - can depend on all packages)
```
**Benefits:**
- **Honest implementation**: The dependency graph accurately reflects what code depends on what, with no hidden or circular relationships that create false expectations
- **Predictable behavior**: Code behaves as the dependency structure suggests - no surprises from circular imports
- **Clear mental model**: Developers can reason about the system without tracking complex circular relationships
- **Build reliability**: No circular dependency errors during compilation or bundling
- **Easier testing**: Lower-level packages can be tested independently without pulling in the entire codebase
### `packages/lib`
**Rules:**
1. No files in `packages/lib` import from `@calcom/app-store` or `../app-store/**`
2. No files in `packages/lib` import from `@calcom/features` or `../features/**`
3. No files in `packages/lib` import from `@calcom/trpc` or `../trpc/**`
4. No files in `packages/lib` import from `@trpc/server`
5. **BONUS:** No repository files allowed in `packages/lib` (repositories belong in features or app-store)
**Incorrect:**
```typescript
// Bad - lib importing from features
import { getBooking } from "@calcom/features/bookings";
import { EventRepository } from "@calcom/features/events/repositories";
// Bad - lib importing from app-store
import { googleCalendarHandler } from "@calcom/app-store/googlecalendar";
// Bad - lib importing from trpc
import { router } from "@calcom/trpc/server/trpc";
```
**Correct:**
```typescript
// Good - lib only imports from other lib files or external packages
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
import type { User } from "@prisma/client";
```
### `packages/app-store`
**Rules:**
6. No files in `packages/app-store` import from `@calcom/features` or `../features/**`
7. No files in `packages/app-store` import from `@calcom/trpc` or `../trpc/**`
**Incorrect:**
```typescript
// Bad - app-store importing from features
import { BookingService } from "@calcom/features/bookings/services";
// Bad - app-store importing from trpc
import { publicProcedure } from "@calcom/trpc/server/trpc";
```
**Correct:**
```typescript
// Good - app-store imports from lib and prisma
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
import type { Credential } from "@prisma/client";
```
### `packages/features`
**Rules:**
8. No files in `packages/features` import from `@calcom/trpc` or `../trpc/**`
9. No files in `packages/features` import from `@calcom/web` or `@calcom/web/**`
10. No files in `packages/features` import from `../../apps/web/**`
**Incorrect:**
```typescript
// Bad - features importing from trpc
import { router } from "@calcom/trpc/server/trpc";
// Bad - features importing from web app
import { getServerSession } from "@calcom/web/lib/auth";
import { WebComponent } from "../../apps/web/components/WebComponent";
```
**Correct:**
```typescript
// Good - features import from lib, app-store, and prisma
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
import { googleCalendarHandler } from "@calcom/app-store/googlecalendar";
```
### `packages/trpc`
**Rules:**
11. No files in `packages/trpc` import from `../apps/web/**`
**Incorrect:**
```typescript
// Bad - trpc importing from web app
import { getServerSession } from "../../apps/web/lib/auth";
```
**Correct:**
```typescript
// Good - trpc imports from features, lib, and prisma
import { BookingService } from "@calcom/features/bookings/services";
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
```
### `packages/testing`
**Rules:**
12. No files in `packages/testing` import from `@calcom/web` or `@calcom/web/**`
13. No files in `packages/testing` import from `@calcom/features` or `@calcom/features/**`
14. No files in `packages/testing` import from `../../apps/web/**`
15. No files in `packages/testing` import from `../features/**`
**Incorrect:**
```typescript
// Bad - testing importing from web or features
import { WebComponent } from "@calcom/web/components";
import { BookingService } from "@calcom/features/bookings";
```
**Correct:**
```typescript
// Good - testing imports from lib and prisma
import { prisma } from "@calcom/prisma";
import { logger } from "@calcom/lib/logger";
```
### `packages/platform/atoms`
**Rules:**
16. No files in `packages/platform/atoms` import from `@calcom/trpc` or `@calcom/trpc/**`
17. No files in `packages/platform/atoms` import from `../../trpc` or `../../trpc/**`
18. No files in `packages/platform/atoms` import from `@calcom/web`
**Incorrect:**
```typescript
// Bad - platform/atoms importing from trpc or web
import { router } from "@calcom/trpc/server/trpc";
import { trpc } from "../../trpc";
import { WebComponent } from "@calcom/web";
```
**Correct:**
```typescript
// Good - platform/atoms imports from lib, features, and ui
import { logger } from "@calcom/lib/logger";
import { Button } from "@calcom/ui/components/button";
import type { Booking } from "@calcom/features/bookings/types";
Trigger.dev is the async task runner used in both `apps/web` and `apps/api/v2`. Tasks can be disabled via the `ENABLE_ASYNC_TASKER` env var (set to `"false"` to fall back to synchronous execution).
## Tasker Architecture
Every Trigger.dev feature follows the Tasker pattern with these layers:
```
packages/features/<domain>/lib/tasker/
types.ts # ITasker interface + payload types
<Domain>Tasker.ts # Tasker subclass (dispatches async or sync)
<Domain>TaskService.ts # Business logic consumed by both
trigger/
config.ts # Queue + retry + machine config
schema.ts # Zod payload schema
<task-name>.ts # schemaTask definition
```
Reference implementation: `packages/features/calendars/lib/tasker/` — see `CalendarsTasker.ts` for the Tasker subclass pattern and `trigger/config.ts` for queue/retry/machine configuration
## Creating a New Task
### 1. Define the Zod schema for payload validation
Always use `schemaTask` with a Zod schema for payload validation:
```typescript
// trigger/schema.ts
import { z } from "zod";
export const myTaskSchema = z.object({
userId: z.number(),
});
```
### 2. Configure queue, retry, and machine
See `packages/features/calendars/lib/tasker/trigger/config.ts` for a real example.
```typescript
// trigger/config.ts
import { type schemaTask, queue } from "@trigger.dev/sdk";
type MyTask = Pick<Parameters<typeof schemaTask>[0], "machine" | "retry" | "queue">;
export const myQueue = queue({
name: "my-domain",
concurrencyLimit: 10,
});
export const myTaskConfig: MyTask = {
machine: "small-2x",
queue: myQueue,
retry: {
maxAttempts: 3,
factor: 2,
minTimeoutInMs: 60000,
maxTimeoutInMs: 300000,
randomize: true,
outOfMemory: {
machine: "medium-1x",
},
},
};
```
### 3. Define the task using schemaTask
```typescript
// trigger/my-task.ts
import { schemaTask, type TaskWithSchema } from "@trigger.dev/sdk";
Concurrency limits control how many task runs execute in parallel within a queue. Getting this right is critical for production stability.
**How to estimate concurrency:**
1. Analyze production logs for the number of requests per minute that will trigger the task (from both `apps/web` and `apps/api/v2`)
2. Measure the average execution time of the task
3. Factor in burst patterns (e.g., peak booking hours)
**Guidelines:**
- Time-sensitive tasks (emails, webhooks) need higher concurrency
- Background jobs that are not time-sensitive can use lower concurrency
- Start conservative and increase based on production metrics
See `packages/features/calendars/lib/tasker/trigger/config.ts` (`concurrencyLimit: 10`) and `packages/features/webhooks/lib/tasker/trigger/config.ts` (`concurrencyLimit: 20`) for real examples of how concurrency is tuned per domain.
2. Run the Trigger.dev CLI from the features package:
```bash
cd packages/features && npx trigger.dev@latest dev --analyze
```
3. Keep the CLI running while developing. Tasks appear in the Trigger.dev dashboard under **Tasks**.
## Before Merging
1. **Deploy to staging**: `cd packages/features && yarn deploy:trigger:staging`
2. **Test on Trigger.dev dashboard**: Switch to staging environment and use the **Test** tab
3. **Right-size machines**: Start with the smallest machine; increase only if you see `OutOfMemory` errors
4. **Set retry with OOM handling**: Always include `outOfMemory` in retry config with a larger machine than the default
5. **Set concurrency**: Analyze production request volume and task completion time to determine the appropriate `concurrencyLimit`
6. **Cherry-pick caveat**: Cherry-picking does not redeploy Trigger.dev tasks. Only the `draft-release` CI action does. If cherry-picking a change that impacts Trigger.dev tasks, manually promote the new version in the Trigger.dev deployment dashboard after the fix is deployed
## Common Mistakes
**Using `task` instead of `schemaTask`:**
```typescript
// Bad - no payload validation
import { task } from "@trigger.dev/sdk";
export const myTask = task({
id: "my-task",
run: async (payload: any) => { ... },
});
// Good - validated payload with Zod
import { schemaTask } from "@trigger.dev/sdk";
export const myTask = schemaTask({
id: "my-task",
schema: myTaskSchema,
run: async (payload) => { ... },
});
```
**Missing retry or queue config:**
```typescript
// Bad - no retry or queue, will use defaults
export const myTask = schemaTask({
id: "my-task",
schema: myTaskSchema,
run: async (payload) => { ... },
});
// Good - explicit config from shared config file
import { myTaskConfig } from "./config";
export const myTask = schemaTask({
id: "my-task",
...myTaskConfig,
schema: myTaskSchema,
run: async (payload) => { ... },
});
```
**Importing eagerly inside task files instead of using dynamic imports:**
```typescript
// Bad - eager import of heavy modules at file scope
import { MyService } from "@calcom/features/domain/service/MyService";
description: Interact with the Cal.com API v2 to manage scheduling, bookings, event types, availability, and calendars. Use this skill when building integrations that need to create or manage bookings, check availability, configure event types, or sync calendars with Cal.com's scheduling infrastructure.
env:
CAL_API_KEY:
description: "Cal.com API key (prefixed with cal_live_ or cal_test_). Required for all API requests."
required: true
CAL_CLIENT_ID:
description: "OAuth client ID for platform integrations managing users on behalf of others. Sent as x-cal-client-id header."
required: false
CAL_SECRET_KEY:
description: "OAuth client secret for platform integrations. Sent as x-cal-secret-key header."
required: false
CAL_WEBHOOK_SECRET:
description: "Secret used to verify webhook payload signatures via X-Cal-Signature-256 header."
required: false
---
# Cal.com API v2
@@ -14,6 +27,15 @@ All API requests should be made to:
https://api.cal.com/v2
```
## Required Credentials
| Environment Variable | Required | Description |
|---------------------|----------|-------------|
| `CAL_API_KEY` | Yes | Cal.com API key (prefixed with `cal_live_` or `cal_test_`). Used as Bearer token for all API requests. Generate from Settings > Developer > API Keys. |
| `CAL_CLIENT_ID` | No | OAuth client ID for platform integrations that manage users on behalf of others. Sent as `x-cal-client-id` header. |
| `CAL_SECRET_KEY` | No | OAuth client secret for platform integrations. Sent as `x-cal-secret-key` header. |
| `CAL_WEBHOOK_SECRET` | No | Secret for verifying webhook payload signatures via the `X-Cal-Signature-256` header. |
## Authentication
All API requests require authentication via Bearer token:
"Accepts application/x-www-form-urlencoded (RFC 6749 standard) or application/json. "+
"Use grant_type 'authorization_code' with client_secret (confidential) or code_verifier (public/PKCE), or grant_type 'refresh_token' with client_secret (confidential) or just the refresh_token (public).",
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.