* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com
This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:
- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions
3893 files changed, 20789 insertions(+), 411020 deletions(-)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* rip out org related comments in api v2
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* docs: add self-hosting troubleshooting guide
Add a dedicated troubleshooting page (docs/self-hosting/troubleshooting.mdx)
covering the most common self-hosting issues:
- 500 error during onboarding caused by missing STRIPE_PRIVATE_KEY (#25993)
- Redirect to localhost after deployment (NEXTAUTH_URL / NEXT_PUBLIC_WEBAPP_URL) (#21921)
- API v2 service not starting in Docker (missing REDIS_URL, JWT_SECRET, WEB_APP_URL)
- CLIENT_FETCH_ERROR in Docker logs
- SSL issues behind a reverse proxy
- Prisma user creation failure on first setup
Also adds the new page to the "Getting Started" navigation group in docs/docs.json.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(docs): provide safer SSL troubleshooting alternatives
Replace the blanket NODE_TLS_REJECT_UNAUTHORIZED=0 recommendation
with three options in order of preference:
1. Use HTTP internally with proper header forwarding
2. Add internal CA to NODE_EXTRA_CA_CERTS
3. Disable TLS verification (last resort with security warning)
The previous guidance could expose users to MITM attacks on all
external API calls (Stripe, Google, etc.).
* fix(docs): correct NEXTAUTH_URL guidance to prevent OAuth breakage
The previous guidance recommended setting NEXTAUTH_URL to localhost
for SSL/DNS issues, which breaks OAuth callbacks since external
providers would redirect to localhost instead of the public domain.
- Replace localhost workaround with extra_hosts in docker-compose
- Add nginx proxy header configuration example
- Add warnings explaining why localhost breaks OAuth
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(docs): correct troubleshooting guide based on codebase verification
- Stripe section: Rewrite to accurately reflect current behavior (app
gracefully handles missing keys by marking Stripe as 'not installed'
rather than crashing). Clarify that NEXT_PUBLIC_STRIPE_PUBLIC_KEY
belongs in .env.appStore.
- API v2 section: Add missing required vars (STRIPE_API_KEY,
STRIPE_WEBHOOK_SECRET, NEXTAUTH_SECRET) that crash the service if
absent. Move WEB_APP_URL to optional (it has a fallback default).
- CLIENT_FETCH_ERROR section: Add caveat about HTTPS URLs failing
with the extra_hosts approach when app listens on port 3000.
- Vercel note: Clarify that NEXTAUTH_URL is auto-inferred via
VERCEL_URL, not just 'left empty'.
- Database section: Replace unverifiable metadata/id advice with
actionable migration and setup guidance matching actual code in
apps/web/app/api/auth/setup/route.ts.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* docs: fix inaccuracies in self-hosting troubleshooting guide
- Add missing CALENDSO_ENCRYPTION_KEY to API v2 required variables
- Fix setup endpoint path from /api/auth/setup to /auth/setup
- Add note about NEXTAUTH_URL auto-derivation from NEXT_PUBLIC_WEBAPP_URL
* fix(docs): correct NEXTAUTH_URL derivation mechanism description
NextAuth infers the base URL from the request's Host header when
NEXTAUTH_URL is not set, not from NEXT_PUBLIC_WEBAPP_URL directly.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
---------
Co-authored-by: shockzM1 <shockz@dsn.so>
Co-authored-by: Paperclip <noreply@paperclip.ing>
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(unified-cal): connection-based unified calendar API with CRUD, freebusy, and list connections
- New GET /v2/calendars/connections endpoint returning all calendar connections with connectionId
- Connection-scoped CRUD: GET/POST/PATCH/DELETE /v2/calendars/connections/{connectionId}/events/*
- Connection-scoped free/busy: GET /v2/calendars/connections/{connectionId}/freebusy
- Legacy calendar-type endpoints: GET/POST/DELETE /v2/calendars/{calendar}/events, GET /{calendar}/freebusy
- Backward compat: dual @Patch decorators for singular /event/ (deprecated) and plural /events/
- ConnectedCalendarEntry interface to eliminate inline type annotations
- DRY service layer with shared private helpers (listEventsWithClient, createEventWithClient, etc.)
- Input validation: @IsDefined() on start/end, @IsTimeZone() on timezone fields, cross-field to >= from validation
- All-day event support: Google Calendar date-only events converted to midnight UTC
- New findCredentialByIdAndUserId method in CredentialsRepository for connection-scoped lookups
* style: apply biome formatting to unified calendar API files
* fix: use @IsTimeZone() validator for timeZone field in CreateEventDateTimeWithZone
* fix: add delegation auth support, extract freebusy service layer
- Comment 3: getCalendarClientForUser and getCalendarClientByCredentialId now
use getAuthorizedCalendarInstance with delegated-auth fallback instead of
requiring credential.key directly. Added findCredentialWithDelegationByTypeAndUserId
and expanded findCredentialByIdAndUserId to include delegationCredentialId.
- Comment 5: Extracted freebusy and connections logic from controller into
UnifiedCalendarsFreebusyService, keeping the controller thin (HTTP-only).
Moved ConnectedCalendarEntry type and INTEGRATION_TYPE_TO_API mapping into
the service layer.
- Biome auto-formatting applied to touched files.
* test: add unit and integration tests for unified calendar API
- GoogleCalendarService: 30 tests covering delegation auth, client creation, CRUD
- UnifiedCalendarsFreebusyService: 21 tests covering connections, busy times, filtering
- CalUnifiedCalendarsController: 31 tests covering all endpoints (connection-scoped + legacy)
- Pipe specs: 37 existing tests continue to pass
Total: 98 tests across 5 suites
* fix: address Devin Review feedback - fix JSDoc and validator pattern
- Fix incorrect JSDoc on listEventsForUser (all-day events ARE included, not skipped)
- Fix IsAfterFrom validator to return false instead of throwing BadRequestException
(preserves standard ValidationPipe error format)
* fix: revert IsAfterFrom to throw BadRequestException per team convention
Cubic AI (confidence 9/10, team feedback): validators should throw
BadRequestException to preserve the API's standard bad-request response
structure, per team convention.
* fix: add calendarId query param to createConnectionEvent for API consistency
All other connection-scoped endpoints accept calendarId; this was the
only one hardcoding 'primary'. Added @ApiQuery decorator and @Query
parameter with ?? 'primary' fallback, plus a test for custom calendarId.
* Update apps/api/v2/src/modules/cal-unified-calendars/controllers/cal-unified-calendars.controller.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Revert "Update apps/api/v2/src/modules/cal-unified-calendars/controllers/cal-unified-calendars.controller.ts"
This reverts commit e18e4621eff46d8ec49e4d03230783ce50b0c0e4.
* feat: enhance calendar service with connection-specific methods and improve API documentation
* test: complete delegation auth tests, document virtual mocks, fix key leak tests
- Item 3: Add 7 comprehensive delegation auth integration tests covering
JWT creation params, email cleaning, fallback scenarios, and error handling
- Item 7: Document why virtual mocks are necessary in all test files
(workspace packages with DB dependencies cannot resolve in Jest)
- Cubic #1: Document getCalendarsForConnection caching and upstream limitation
- Cubic #2+#3: Make credential key leak tests non-vacuous by including
actual key fields in mocks and verifying they don't leak
- Remove unused BadRequestException import from freebusy service
* fix: add defense-in-depth key stripping in listConnections controller
Controller now destructures only { connectionId, type, email } from each
connection before returning, so credential.key can never leak even if the
service layer has a future regression. Test updated to verify stripping.
* feat: add unified calendar API endpoints for connections and events management
* fix: add try/catch error handling to CRUD helper methods
Wrap Google Calendar API calls in listEventsWithClient, createEventWithClient,
getEventWithClient, updateEventWithClient, and deleteEventWithClient with
try/catch blocks matching the legacy getEventDetails/updateEventDetails pattern.
This ensures proper NestJS exceptions (NotFoundException, BadRequestException)
are returned instead of raw 500 errors when the Google API throws.
* fix: map Google API errors to correct HTTP status codes
Replace blanket NotFoundException/BadRequestException in CRUD catch blocks
with mapGoogleApiError() that inspects the GaxiosError status code and
returns the appropriate NestJS exception (404→NotFoundException,
401/403→UnauthorizedException, 400→BadRequestException, else→500).
* fix: preserve upstream Google API status codes in error mapping
Separate 403 (ForbiddenException) from 401 (UnauthorizedException) and
add 429 rate-limit handling. This ensures permission-denied and throttling
errors are not misreported to API clients.
* fix: distinguish Google quota/rate-limit 403 from permission 403
Check GaxiosError reason field for rateLimitExceeded, userRateLimitExceeded,
and dailyLimitExceeded before mapping 403 to ForbiddenException. Quota
errors are now correctly mapped to 429 (retriable) instead.
* fix: keep dailyLimitExceeded as 403 (non-retriable quota exhaustion)
dailyLimitExceeded is a daily quota cap, not transient throttling.
Only rateLimitExceeded and userRateLimitExceeded are remapped to 429.
* fix: add missing @ApiQuery decorators for calendarId on get/update/delete endpoints
getConnectionEvent, updateConnectionEvent, and deleteConnectionEvent were
missing @ApiQuery({ name: 'calendarId', required: false }) which caused
OpenAPI spec to incorrectly mark calendarId as required.
* ci: retry flaky vitest worker test
* fix: update calendarId query parameter to be optional in OpenAPI specification
* fix: swap dual decorator order so plural /events/ path appears in OpenAPI spec
NestJS Swagger only picks up the first HTTP method decorator. Swapping
the order ensures the preferred plural path (/events/:eventUid) is
generated in the OpenAPI spec, while the deprecated singular path
(/event/:eventUid) still works at runtime.
* fix: split dual decorators into separate methods so both paths appear in OpenAPI spec
NestJS Swagger only picks up the first HTTP method decorator per handler.
Split getCalendarEventDetails and updateCalendarEvent into separate
methods for the singular /event/ (deprecated) and plural /events/ paths,
each delegating to a shared private helper. Both routes now appear in
the generated OpenAPI spec.
* fix: update openapi.json with split dual-decorator paths for GET/PATCH event endpoints
* fix: mapGoogleApiError - coerce string code to number and read errors from response.data
* fix: mapGoogleApiError - guard against NaN from non-numeric error codes
* fix: use read replica for findCredentialWithDelegationByTypeAndUserId query
* refactor: address review comments - UnifiedCalendarService, ParseConnectionIdPipe, thin controller
- Comment 70 (Ryukemeister): Remove 'what' JSDoc from calendars.service.ts
- Comment 71 (Ryukemeister): Use array syntax for dual paths instead of separate methods
- Comments 73-78 (ThyMinimalDev): Create ParseConnectionIdPipe for connectionId validation
- Comments 79-84 (ThyMinimalDev): Create UnifiedCalendarService with strategy pattern
- Comment 85 (ThyMinimalDev): Move getConnections from freebusy to UnifiedCalendarService
- Controller now only handles HTTP concerns, delegates all logic to UnifiedCalendarService
- Updated all test specs to match refactored architecture
* chore: regenerate openapi.json after controller refactor to array syntax paths
---------
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* feat: add booking attendees endpoint to API v2
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* feat: add rate limiting to booking attendees endpoint
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* refactor: simplify attendees output to id, bookingId, name, email, timeZone
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* test: add E2E tests for booking attendees endpoint
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* chore: update bookings repository
* fixup: add pbac guards and update service logic
* chore: update openapi spec
* test: add rate limiting E2E test for booking attendees endpoint
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: tests
* fix: return 404 instead of 403 for non-existent booking in BookingPbacGuard
The BookingPbacGuard was returning 403 (Forbidden) for non-existent bookings
because doesUserIdHaveAccessToBooking returns false when a booking doesn't
exist, which the guard treated as an access denial.
Added an explicit booking existence check in the guard before the access
check, so non-existent bookings now correctly return 404 (Not Found) as
documented in the PR description.
Updated the E2E test to expect 404 for non-existent booking UIDs.
Issue identified by cubic.
Co-Authored-By: unknown <>
* fixup
* fix: return 404 instead of 403 for non-existent booking in attendees endpoint
BookingPbacGuard now checks booking existence before the access check,
returning 404 (Not Found) instead of 403 (Forbidden) for non-existent
booking UIDs. Updated the E2E test assertion and description to match.
Issue identified by cubic (confidence 9/10).
Co-Authored-By: unknown <>
* chore: implement PR feedback
* chore: update tests
* fixup
* chore: update endpoint decsription
* feat: endpoint to retrieve specific attendee
* chore: update e2e tests
* chore: implement cubic feedback
* fix: update test to expect 403 for non-existent booking UID (BookingPbacGuard behavior)
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: merge conflicts
* feat: endpoint to get attendees
* chore: update findByUidIncludeEventTypeAttendeesAndUser method
* chore: implement PR feedback
* fix: e2e tests
* chore: update e2e tests
* fixup fixup
* fix: remove phoneNumber assertion since it's optional and not provided in test
* chore: implement PR feedback
* fix: keep the same output shape for get attendees and get attendee endpoint
* chore: update openapi spec
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* fix: ensure default calendars
* test: add E2E tests for delegation credential controller and update tasker config
- Add E2E tests to verify ensureDefaultCalendars is called when enabling delegation credentials
- Update calendars tasker config to use medium-1x machine for retry on OOM
- Set minimum retry backoff to 60 seconds (1 minute between retries)
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: update tasker config to use small-2x machine with outOfMemory retry on medium-1x
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: update E2E tests to properly spy on service instance and use valid workspace platform slug
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* ci: add CALCOM_SERVICE_ACCOUNT_ENCRYPTION_KEY to E2E API v2 workflow
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: add encryption key to E2E test file for delegation credentials
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* revert: remove CALCOM_SERVICE_ACCOUNT_ENCRYPTION_KEY from workflow (moved to test file)
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: move encryption key to setEnvVars.ts for E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use valid format for service account encryption key
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: encrypt service account key in E2E test for delegation credentials
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: mock updateDelegationCredentialEnabled to bypass Google API call in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: get service from app.get() after initialization for proper spy setup in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use jest.mock() to mock toggleDelegationCredentialEnabled and bypass Google API calls in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use Service.prototype pattern for spying on ensureDefaultCalendars in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: move spy setup to beforeAll before app.init() for proper NestJS interception
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add guest limits and rate limiting to booking-guests endpoint
- Add ArrayMaxSize(10) validation to limit guests per request to 10
- Add aggressive rate limiting (5 requests/minute) via @Throttle decorator
- Add total guest limit check (max 30 guests per booking) to prevent abuse
- Update API documentation to reflect new limits
This prevents scammers from using the endpoint to send spam emails
to hundreds of guests through our system.
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* docs: update openapi.json with guest limits and rate limiting info
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: ensure default calendars with trigger.dev apiv2
* test: add unit and e2e tests for CalendarsTasker integration
- Add unit tests for CalendarsTasker.dispatch when enableAsyncTasker is true
- Add e2e test to verify ensureDefaultCalendarsForUser is called when creating membership
- Mock CalendarsTasker and ConfigService in unit tests
- Test both async (Trigger.dev) and sync (Bull queue) paths
- Fix missing return types on helper functions in e2e tests
- Add *.spec.ts to biome test file exceptions for noExcessiveLinesPerFunction rule
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The UpdateInputAddressLocation_2024_08_13 and related location types were
referenced via getSchemaPath() but not registered with @ApiExtraModels,
causing them to be missing from the generated OpenAPI spec.
This fix adds @ApiExtraModels decorator to the BookingLocationController
to register all location types used in the UpdateBookingLocationInput.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* docs: improve beforeEventBuffer and afterEventBuffer descriptions in API v2
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fixup! docs: improve beforeEventBuffer and afterEventBuffer descriptions in API v2
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Documentation edits made through Mintlify web editor
* Documentation edits made through Mintlify web editor
---------
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* clean-up nav
* Documentation edits made through Mintlify web editor
* Documentation edits made through Mintlify web editor
* further clean up
* add oauth in sidebar
* --
* land in v2
* land in v2
* Documentation edits made through Mintlify web editor
* Documentation edits made through Mintlify web editor
* cleanup
* Documentation edits made through Mintlify web editor
* deprecated v1
---------
Co-authored-by: Syed Ali Shahbaz <alishahbaz7@gmail.com>
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* chore: tag deprecated platform oauth endpoints in api v2
* fixup! chore: tag deprecated platform oauth endpoints in api v2
* fixup! fixup! chore: tag deprecated platform oauth endpoints in api v2
* chore: fix docs.json mintlify
* chore: fix docs.json mintlify
Platform organizations don't have public-facing subdomains, so non-managed
users in platform orgs should get cal.com URLs instead of the platform
org subdomain.
- Updated EventTypeUser type to include isPlatform field
- Modified buildBookingUrl to check isPlatform before using org slug
- Added unit test for platform org users
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>