fix(api): skip platform org subdomain in bookingUrl for API v2 (#26812)

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>
This commit is contained in:
Dhairyashil Shinde
2026-01-14 09:04:45 +00:00
committed by GitHub
co-authored by Morgan
parent 3860717ae3
commit 821dce6a04
3 changed files with 63 additions and 20 deletions
@@ -82,6 +82,35 @@ describe("OutputEventTypesService_2024_06_14", () => {
expect(result).toBe("https://acme.cal.com/owner1/30min");
});
it("should use cal.com for non-managed users in platform orgs", () => {
const user = {
id: 1,
name: "Dhairyashil Shinde",
username: "dhairyashil10101010-gmail-com",
avatarUrl: null,
brandColor: null,
darkBrandColor: null,
weekStart: "Monday",
metadata: {},
organizationId: 1,
organization: { slug: "gmail-platform-9041df0e" },
movedToProfile: null,
profiles: [
{
id: 100,
username: "dhairyashil",
organizationId: 1,
organization: { id: 1, slug: "gmail-platform-9041df0e", isPlatform: true },
},
],
};
const slug = "secret";
const result = service.buildBookingUrl(user, slug);
expect(result).toBe("https://cal.com/dhairyashil/secret");
});
it("should fall back to user username when profile has no username", () => {
const user = {
id: 1,
@@ -53,7 +53,7 @@ type EventTypeUser = {
weekStart: string;
metadata: Prisma.JsonValue;
organizationId: number | null;
organization?: { slug: string | null } | null;
organization?: { slug: string | null; isPlatform?: boolean } | null;
movedToProfile?: ProfileMinimal | null;
profiles?: ProfileMinimal[];
};
@@ -448,7 +448,9 @@ export class OutputEventTypesService_2024_06_14 {
return "";
}
const orgSlug = profile?.organization?.slug ?? null;
const org = profile?.organization;
// Don't use org subdomain for platform organizations - they don't have public-facing subdomains
const orgSlug = org && !org.isPlatform ? org.slug : null;
const webAppUrl = this.configService.get<string>("app.baseUrl", "https://app.cal.com");
const baseUrl = orgSlug ? getBookerBaseUrlSync(orgSlug) : webAppUrl;
const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
+30 -18
View File
@@ -2936,7 +2936,9 @@
"watchlist.create",
"watchlist.read",
"watchlist.update",
"watchlist.delete"
"watchlist.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
}
@@ -6659,7 +6661,9 @@
"webhook.create",
"webhook.read",
"webhook.update",
"webhook.delete"
"webhook.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
}
@@ -29645,7 +29649,9 @@
"watchlist.create",
"watchlist.read",
"watchlist.update",
"watchlist.delete"
"watchlist.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
},
@@ -29758,7 +29764,9 @@
"watchlist.create",
"watchlist.read",
"watchlist.update",
"watchlist.delete"
"watchlist.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
},
@@ -29920,7 +29928,9 @@
"watchlist.create",
"watchlist.read",
"watchlist.update",
"watchlist.delete"
"watchlist.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
},
@@ -30039,7 +30049,9 @@
"watchlist.create",
"watchlist.read",
"watchlist.update",
"watchlist.delete"
"watchlist.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
}
@@ -30123,7 +30135,9 @@
"webhook.create",
"webhook.read",
"webhook.update",
"webhook.delete"
"webhook.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
},
@@ -30215,7 +30229,9 @@
"webhook.create",
"webhook.read",
"webhook.update",
"webhook.delete"
"webhook.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
},
@@ -30356,7 +30372,9 @@
"webhook.create",
"webhook.read",
"webhook.update",
"webhook.delete"
"webhook.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
},
@@ -30454,7 +30472,9 @@
"webhook.create",
"webhook.read",
"webhook.update",
"webhook.delete"
"webhook.delete",
"featureOptIn.read",
"featureOptIn.update"
]
}
}
@@ -33058,10 +33078,6 @@
"error"
]
},
"message": {
"type": "string",
"example": "This endpoint will require authentication in a future release."
},
"error": {
"type": "object"
},
@@ -33098,10 +33114,6 @@
"type": "string"
}
},
"message": {
"type": "string",
"example": "This endpoint will require authentication in a future release."
},
"error": {
"type": "object"
}