Files
cal.diy/packages/features/ee/organizations
bc665cbeab fix: APIV2 team membership - Member not getting added to event-type automatically (#24780)
* fix: APIV2 team membership addition

* feat: Add trimming for email domain and orgAutoAcceptEmail in auto-accept logic

- Trim whitespace from both user email domain and orgAutoAcceptEmail
- Ensures consistent matching even with accidental whitespace
- Addresses feedback from PR review

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* simplify

* feat: Use shared OrganizationMembershipService in TRPC for consistent auto-accept logic

- Create OrganizationMembershipService.container.ts for DI in TRPC
- Update getOrgConnectionInfo to apply trimming + case-insensitive comparison
- Precompute auto-accept decisions in createNewUsersConnectToOrgIfExists using the service
- Use service in handleNewUsersInvites for consistent auto-accept determination
- Ensures both API v2 and TRPC paths use identical trimming and normalization logic

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* Revert "feat: Use shared OrganizationMembershipService in TRPC for consistent auto-accept logic"

This reverts commit 0b2bd28b6e32e8c1d3dea139ca8ff9cbf402ac00.

* refactor: Unify OrganizationRepository and remove duplicate PrismaOrganizationRepository (#24869)

* refactor: Convert OrganizationRepository from static to instance methods

- Add constructor accepting deps object with prismaClient
- Convert all static methods to instance methods
- Add getOrganizationAutoAcceptSettings method
- Create singleton instance export in repository barrel file
- Update API v2 OrganizationsRepository to extend from OrganizationRepository
- Update all call sites to use singleton instance
- Add platform-libraries organizations.ts export
- Fix mock imports to use repository barrel
- Fix unsafe optional chaining in next-auth-options.ts
- Fix any types in test files with proper type inference

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Update all imports to use OrganizationRepository barrel export

- Update imports from direct OrganizationRepository file to barrel export
- This ensures mocks work correctly in tests
- Fixes 202 failing tests related to organizationRepository mock

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Update test mocks to use partial mock pattern

- Convert organizationMock to partial mock that preserves real class
- Add proper prisma mocks to failing test files
- Remove old OrganizationRepository mocks from test files
- This fixes test failures related to mock interception

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Export mocked singleton and update tests to use it directly

- Export mockedSingleton as organizationRepositoryMock from organizationMock
- Update delegationCredential.test.ts to import and use the exported mock
- This fixes 'vi.mocked(...).mockResolvedValue is not a function' errors

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Use platform-libraries import for API v2 OrganizationRepository

API v2 should import shared features through @calcom/platform-libraries
instead of directly from @calcom/features to maintain proper architectural
boundaries and packaging/licensing separation.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* refactor: Implement DI pattern for OrganizationRepository

- Create OrganizationRepository.module.ts and .container.ts for DI
- Replace singleton pattern with getOrganizationRepository() across 20 files
- Update platform-libraries to export getOrganizationRepository
- Delete duplicate PrismaOrganizationRepository.ts
- Remove singleton export file (repositories/index.ts)
- Update test mocks to use DI container pattern
- All type checks and unit tests passing

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Implement read/write client separation in OrganizationRepository

- Updated OrganizationRepository constructor to accept optional prismaWriteClient parameter
- Routed all write operations (create, update) through prismaWrite client
- Routed all read operations (find, get) through prismaRead client
- Updated API v2 OrganizationsRepository to pass both dbRead.prisma and dbWrite.prisma to super()
- Optimized getOrganizationRepository() calls by storing in local variables to avoid repeated function calls
- This fixes the critical issue where API v2 was passing read-only client to base class with write methods

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Fix mock setup and optimize getOrganizationRepository() calls

- Fixed verify-email.test.ts mock to return mocked repository instance instead of scenario helper object
- Added mockReset to organizationMock.ts beforeEach to properly reset mock implementations between tests
- Added local variables in page.tsx to store getOrganizationRepository() result for consistency

This fixes the issue where getOrganizationRepository() was returning organizationScenarios.organizationRepository (scenario helper) instead of the actual mocked repository instance, causing findUniqueNonPlatformOrgsByMatchingAutoAcceptEmail to be undefined.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* refactor: Simplify OrganizationRepository to use single prismaClient

- Updated base OrganizationRepository constructor to accept only { prismaClient } instead of { prismaClient, prismaWriteClient? }
- Replaced this.prismaRead and this.prismaWrite with single this.prismaClient property
- Updated API v2 OrganizationsRepository to pass only dbWrite.prisma as prismaClient
- Removed unused PrismaReadService import from API v2
- All read and write operations now use the same client instance

This simplifies the architecture as requested - API v2 uses write client for all operations, and apps/web uses the client from DI container.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: Match OrganizationsRepository.findById signature with base class

The findById method in OrganizationsRepository was using a different signature
than the base OrganizationRepository class, causing type errors in CI.

Changed from: findById(organizationId: number)
Changed to: findById({ id }: { id: number })

This matches the base class signature and resolves the CI unit test failures.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Update all findById call sites to use object parameter

Fixed 6 call sites in API v2 that were calling findById with a number
instead of the required { id: number } object parameter:

- is-org.guard.ts
- is-admin-api-enabled.guard.ts
- is-webhook-in-org.guard.ts
- organizations.service.ts
- managed-organizations.service.ts (2 call sites)

This resolves the API v2 build failure caused by the signature change
in OrganizationsRepository.findById to match the base class.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: Remove redundant findById override from OrganizationsRepository

The findById method was duplicating the base class OrganizationRepository
implementation. Both methods had identical logic (filtering by isOrganization: true),
so the override was unnecessary.

Since OrganizationsRepository extends OrganizationRepository and passes
dbWrite.prisma to the base constructor, the base class method already
provides the exact same functionality.

This resolves the API v2 build failure by eliminating the duplicate method
that was causing conflicts.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* Remove unncessary changes

* Store in variable

* Revert "Remove unncessary changes"

This reverts commit af9351786a21616c9508c441191c17f2374fb2cc.

* Revert dbRead/dbWrite changes

* Add organizations library to tsconfig.json

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-11-07 10:50:00 +00:00
..

Organizations feature

From the Original RFC:

We want to create organisations within Cal.com to enable people to easily and effectively manage multiple teams. An organisation will live above the current teams layer.

App setup

  1. Log in as admin and in Settings, turn on Organizations feature flag under Features section. Organizations feature has an operational feature flag in order to turn on the entire feature.

  2. Set the following environment variables as described:

    1. CALCOM_LICENSE_KEY: Since Organizations is an EE feature, a license key should be present, either as this environment variable or visiting as an Admin /auth/setup. To get a license key, please contact sales
    2. NEXT_PUBLIC_WEBAPP_URL: In case of local development, this variable should be set to http://app.cal.local:3000 to be able to handle subdomains correctly in terms of authentication and cookies
    3. NEXTAUTH_URL: Should be equal to NEXT_PUBLIC_WEBAPP_URL which is http://app.cal.local:3000
    4. NEXTAUTH_COOKIE_DOMAIN: In case of local development, this variable should be set to .cal.local to be able to accept session cookies in subdomains as well otherwise it should be set to the corresponding environment such as .cal.dev, .cal.qa or .cal.com. If you choose another subdomain, the value for this should match the apex domain of NEXT_PUBLIC_WEBAPP_URL with a leading dot (.)
    5. ORGANIZATIONS_ENABLED: Should be set to 1 or true
    6. STRIPE_ORG_MONTHLY_PRICE_ID: For dev and all testing should be set to your own testing key. Or ask for the shared key if you're a core member.
    7. ORGANIZATIONS_AUTOLINK: Optional. Set to 1 or true to let new signed-up users using Google external provider join the corresponding organization based on the email domain name.
  3. Add app.cal.local to your host file, either:

    1. sudo npx hostile set localhost app.cal.local
    2. Add it yourself
  4. Add acme.cal.local to host file given that the org create for it will be acme, otherwise do this for whatever slug will be assigned to the org. This is needed to test org-related public URLs, such as sub-teams, members and event-types.

  5. Be sure to be logged in with any type of user and visit /settings/organizations/new and follow setup steps with the slug matching the org slug from step 3

  6. Log in as admin and go to Settings and under Organizations you will need to accept the newly created organization in order to be operational

  7. After finishing the org creation, you will be automatically logged in as the owner of the organization, and the app will be shown in organization mode

Note

Browsers do not allow camera/mic access on any non-HTTPS hosts except for localhost. To test cal video organization meeting links locally (app.cal.local/video/[uid]). You can access the meeting link by replacing app.cal.local with localhost in the URL.

For eg:- Use http://localhost:3000/video/nAjnkjejuzis99NhN72rGt instead of http://app.cal.local:3000/video/nAjnkjejuzis99NhN72rGt.

To get an HTTPS URL for localhost, you can use a tunneling tool such as ngrok or Tunnelmole . Alternatively, you can generate an SSL certificate for your local domain using mkcert. Turn off any SSL certificate validation in your HTTPS client (be sure to do this for local only, otherwise its a security risk).

Tunnelmole - Open Source Tunnelling Tool:

To install Tunnelmole, execute the command:

curl -O https://install.tunnelmole.com/8dPBw/install && sudo bash install

After a successful installation, you can run Tunnelmole using the following command, replacing 8000 with your actual port number if it is different:

tmole 8000

In the output, you'll see two URLs, one HTTP and an HTTPS URL. For privacy and security reasons, it is recommended to use the HTTPS URL.

View the Tunnelmole README for additional information and other installation methods such as npm or building your own binaries from source.

ngrok - Closed Source Tunnelling Tool:

ngrok is a popular closed source tunneling tool. You can run ngrok using the same port, using the format ngrok http <port> replacing <port> with your actual port number. For example:

ngrok http 8000

This will generate a public URL that you can use to access your localhost server.

DNS setup

When a new organization is created, other than not being verified up until the admin accepts it in settings as explained in step 6, a flag gets created that marks the organization as missing DNS setup. That flag get auto-checked by the system upon organization creation when the Cal instance is deployed in Vercel and the subdomain registration was successful. Logging in as admin and going to Settings > Organizations section, you will see that flag as a badge, designed to give admins a glimpe on what is pending in terms of making an organization work. Alongside the mentioned badge, an email gets sent to admins in order to warn them there is a pending action about setting up DNS for the newly created organization to work. Read more about the Organization Setup here