diff --git a/apps/api/v2/src/modules/auth/guards/or-guard/index.ts b/apps/api/v2/src/modules/auth/guards/or-guard/index.ts new file mode 100644 index 00000000000000..ccfe7f590b36b2 --- /dev/null +++ b/apps/api/v2/src/modules/auth/guards/or-guard/index.ts @@ -0,0 +1 @@ +export { Or } from "./or.guard"; diff --git a/apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts b/apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts new file mode 100644 index 00000000000000..d6c35e081478ad --- /dev/null +++ b/apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts @@ -0,0 +1,104 @@ +import { ExecutionContext, CanActivate } from "@nestjs/common"; + +import { Or } from "./or.guard"; + +// Mock guards for testing +class MockGuard1 implements CanActivate { + constructor(private shouldPass: boolean) {} + + async canActivate(): Promise { + return this.shouldPass; + } +} + +class MockGuard2 implements CanActivate { + constructor(private shouldPass: boolean) {} + + async canActivate(): Promise { + return this.shouldPass; + } +} + +class MockGuard3 implements CanActivate { + constructor(private shouldThrow: boolean = false) {} + + async canActivate(): Promise { + if (this.shouldThrow) { + throw new Error("Guard failed"); + } + return false; + } +} + +describe("OrGuard", () => { + let guard: InstanceType>; + let mockExecutionContext: ExecutionContext; + let mockModuleRef: any; + + beforeEach(() => { + mockModuleRef = { + get: jest.fn(), + }; + + const OrGuardClass = Or([MockGuard1, MockGuard2]); + guard = new OrGuardClass(mockModuleRef); + mockExecutionContext = {} as ExecutionContext; + }); + + it("should be defined", () => { + expect(guard).toBeDefined(); + }); + + it("should grant access when first guard passes", async () => { + const mockGuard1 = new MockGuard1(true); + const mockGuard2 = new MockGuard2(false); + + mockModuleRef.get.mockReturnValueOnce(mockGuard1).mockReturnValueOnce(mockGuard2); + + const result = await guard.canActivate(mockExecutionContext); + + expect(result).toBe(true); + }); + + it("should grant access when second guard passes", async () => { + const mockGuard1 = new MockGuard1(false); + const mockGuard2 = new MockGuard2(true); + + mockModuleRef.get.mockReturnValueOnce(mockGuard1).mockReturnValueOnce(mockGuard2); + + const result = await guard.canActivate(mockExecutionContext); + + expect(result).toBe(true); + }); + + it("should deny access when all guards fail", async () => { + const mockGuard1 = new MockGuard1(false); + const mockGuard2 = new MockGuard2(false); + + mockModuleRef.get.mockReturnValueOnce(mockGuard1).mockReturnValueOnce(mockGuard2); + + const result = await guard.canActivate(mockExecutionContext); + + expect(result).toBe(false); + }); + + it("should continue checking other guards when one throws an error", async () => { + const mockGuard1 = new MockGuard3(true); // throws error + const mockGuard2 = new MockGuard2(true); // passes + + mockModuleRef.get.mockReturnValueOnce(mockGuard1).mockReturnValueOnce(mockGuard2); + + const result = await guard.canActivate(mockExecutionContext); + + expect(result).toBe(true); + }); +}); + +describe("Or decorator", () => { + it("should create a guard class with the specified guards", () => { + const OrGuardClass = Or([MockGuard1, MockGuard2]); + + expect(OrGuardClass).toBeDefined(); + expect(typeof OrGuardClass).toBe("function"); + }); +}); diff --git a/apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts b/apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts new file mode 100644 index 00000000000000..3297f6b2206fdd --- /dev/null +++ b/apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts @@ -0,0 +1,49 @@ +import { Injectable, CanActivate, ExecutionContext, Type, Logger } from "@nestjs/common"; +import { ModuleRef } from "@nestjs/core"; + +/** + * Decorator function that creates an Or guard with the specified guards + * @param guards Array of guard classes to evaluate with OR logic + * @returns A guard class that grants access if ANY of the provided guards return true + */ +export function Or(guards: Type[]) { + @Injectable() + class OrGuard implements CanActivate { + public readonly logger = new Logger("OrGuard"); + + constructor(public readonly moduleRef: ModuleRef) {} + + async canActivate(context: ExecutionContext): Promise { + let lastError: unknown | null = null; + for (const Guard of guards) { + try { + const guardInstance = this.moduleRef.get(Guard, { strict: false }); + const result = await Promise.resolve(guardInstance.canActivate(context)); + + if (result === true) { + this.logger.log(`OrGuard - Guard ${Guard.name} granted access`); + return true; // Access granted if any guard returns true + } + } catch (error) { + lastError = error; + // If a guard throws an exception, it implies failure for that specific guard. + // We catch it and continue checking other guards in the OR chain. + // If an exception should stop the entire chain immediately, re-throw it here. + this.logger.log( + `OrGuard - Guard ${Guard.name} failed: ${ + error instanceof Error ? error.message : "Unknown error" + }` + ); + } + } + + this.logger.log("OrGuard - All guards failed, access denied"); + if (lastError) { + throw lastError; + } + return false; + } + } + + return OrGuard; +} diff --git a/apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts b/apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts new file mode 100644 index 00000000000000..1372b72e419f78 --- /dev/null +++ b/apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts @@ -0,0 +1,39 @@ +import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy"; +import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; +import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common"; +import { Request } from "express"; + +import { Team } from "@calcom/prisma/client"; + +@Injectable() +export class IsUserRoutingForm implements CanActivate { + constructor(private readonly dbRead: PrismaReadService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const routingFormId: string = request.params.routingFormId; + const user = request.user as ApiAuthGuardUser; + if (!routingFormId) { + throw new ForbiddenException("IsUserRoutingForm - No routing form id found in request params."); + } + + const userRoutingForm = await this.dbRead.prisma.app_RoutingForms_Form.findFirst({ + where: { + id: routingFormId, + userId: Number(user.id), + teamId: null, + }, + select: { + id: true, + }, + }); + + if (!userRoutingForm) { + throw new ForbiddenException( + `Routing Form with id=${routingFormId} is not a user Routing Form owned by user with id=${user.id}.` + ); + } + + return true; + } +} diff --git a/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts b/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts index aeda019904e70f..37ddd222b8f318 100644 --- a/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts @@ -23,27 +23,48 @@ describe("OrganizationsRoutingFormsResponsesController", () => { let app: INestApplication; let prismaWriteService: PrismaWriteService; let org: Team; - let team: Team; - let apiKeyString: string; - let routingForm: App_RoutingForms_Form; - let routingFormResponse: App_RoutingForms_FormResponse; - let routingFormResponse2: App_RoutingForms_FormResponse; + + // Grouped data structures + let orgAdminData: { + user: User; + apiKey: string; + eventType: { + id: number; + slug: string | null; + teamId: number | null; + userId: number | null; + title: string; + }; + routingForm: App_RoutingForms_Form; + }; + + let teamData: { + team: Team; + routingForm: App_RoutingForms_Form; + routingFormResponse1: App_RoutingForms_FormResponse; + routingFormResponse2: App_RoutingForms_FormResponse; + }; + + let nonOrgAdminData: { + user: User; + apiKey: string; + eventType: { + id: number; + slug: string | null; + teamId: number | null; + userId: number | null; + title: string; + }; + routingForm: App_RoutingForms_Form; + }; let apiKeysRepositoryFixture: ApiKeysRepositoryFixture; let teamRepositoryFixture: TeamRepositoryFixture; let userRepositoryFixture: UserRepositoryFixture; let organizationsRepositoryFixture: OrganizationRepositoryFixture; - let user: User; const userEmail = `OrganizationsRoutingFormsResponsesController-key-bookings-2024-08-13-user-${randomString()}@api.com`; let profileRepositoryFixture: ProfileRepositoryFixture; - let routingEventType: { - id: number; - slug: string | null; - teamId: number | null; - userId: number | null; - title: string; - }; let membershipsRepositoryFixture: MembershipRepositoryFixture; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ @@ -64,25 +85,34 @@ describe("OrganizationsRoutingFormsResponsesController", () => { isOrganization: true, }); - team = await teamRepositoryFixture.create({ + // Initialize grouped data structures + orgAdminData = {} as any; + teamData = {} as any; + nonOrgAdminData = {} as any; + + teamData.team = await teamRepositoryFixture.create({ name: "OrganizationsRoutingFormsResponsesController orgs booking 1", isOrganization: false, parent: { connect: { id: org.id } }, }); - user = await userRepositoryFixture.create({ + orgAdminData.user = await userRepositoryFixture.create({ email: userEmail, }); + nonOrgAdminData.user = await userRepositoryFixture.create({ + email: `non-org-admin-user-${randomString()}@api.com`, + }); + await membershipsRepositoryFixture.create({ role: "OWNER", - user: { connect: { id: user.id } }, + user: { connect: { id: orgAdminData.user.id } }, team: { connect: { id: org.id } }, accepted: true, }); await profileRepositoryFixture.create({ - uid: `usr-${user.id}`, + uid: `usr-${orgAdminData.user.id}`, username: userEmail, organization: { connect: { @@ -91,98 +121,166 @@ describe("OrganizationsRoutingFormsResponsesController", () => { }, user: { connect: { - id: user.id, + id: orgAdminData.user.id, }, }, }); const now = new Date(); now.setDate(now.getDate() + 1); - const { keyString } = await apiKeysRepositoryFixture.createApiKey(user.id, null); - apiKeyString = `${keyString}`; + const { keyString } = await apiKeysRepositoryFixture.createApiKey(orgAdminData.user.id, null); + orgAdminData.apiKey = `${keyString}`; + + const { keyString: _nonOrgAdminUserApiKey } = await apiKeysRepositoryFixture.createApiKey( + nonOrgAdminData.user.id, + null + ); + nonOrgAdminData.apiKey = `${_nonOrgAdminUserApiKey}`; // Create an event type for routing form to route to - routingEventType = await prismaWriteService.prisma.eventType.create({ + orgAdminData.eventType = await prismaWriteService.prisma.eventType.create({ data: { title: "Test Event Type", slug: "test-event-type", length: 30, - userId: user.id, - teamId: team.id, + userId: orgAdminData.user.id, + teamId: null, }, }); - routingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + nonOrgAdminData.eventType = await prismaWriteService.prisma.eventType.create({ data: { - name: "Test Routing Form", - description: "Test Description", - disabled: false, - routes: [ - { + title: "Test Event Type", + slug: "test-event-type", + length: 30, + userId: nonOrgAdminData.user.id, + teamId: null, + }, + }); + + const routingFormData = { + name: "Test Routing Form", + description: "Test Description", + disabled: false, + routes: [ + { + id: "route-1", + queryValue: { id: "route-1", - queryValue: { - id: "route-1", - type: "group", - children1: { - "rule-1": { - type: "rule", - properties: { - field: "question1", - operator: "equal", - value: ["answer1"], - valueSrc: ["value"], - valueType: ["text"], - }, + type: "group", + children1: { + "rule-1": { + type: "rule", + properties: { + field: "question1", + operator: "equal", + value: ["answer1"], + valueSrc: ["value"], + valueType: ["text"], }, }, }, + }, + action: null, + isFallback: false, + }, + { + id: "fallback-route", + action: { type: "customPageMessage", value: "Fallback Message" }, + isFallback: true, + queryValue: { id: "fallback-route", type: "group" }, + }, + ], + fields: [ + { + id: "question1", + type: "text", + label: "Question 1", + required: true, + identifier: "question1", + }, + { + id: "question2", + type: "text", + label: "Question 2", + required: false, + identifier: "question2", + }, + ], + settings: { + emailOwnerOnSubmission: false, + }, + }; + + orgAdminData.routingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + ...routingFormData, + routes: [ + { + ...routingFormData.routes[0], action: { type: "eventTypeRedirectUrl", - eventTypeId: routingEventType.id, - value: `team/${team.slug}/${routingEventType.slug}`, + eventTypeId: orgAdminData.eventType.id, + value: `${orgAdminData.user.username}/${orgAdminData.eventType.slug}`, }, - isFallback: false, - }, - { - id: "fallback-route", - action: { type: "customPageMessage", value: "Fallback Message" }, - isFallback: true, - queryValue: { id: "fallback-route", type: "group" }, }, + routingFormData.routes[1], ], - fields: [ + userId: orgAdminData.user.id, + // User Routing Form has teamId=null + teamId: null, + }, + }); + + nonOrgAdminData.routingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + ...routingFormData, + routes: [ { - id: "question1", - type: "text", - label: "Question 1", - required: true, - identifier: "question1", + ...routingFormData.routes[0], + action: { + type: "eventTypeRedirectUrl", + eventTypeId: nonOrgAdminData.eventType.id, + value: `${nonOrgAdminData.user.username}/${nonOrgAdminData.eventType.slug}`, + }, }, + routingFormData.routes[1], + ], + userId: nonOrgAdminData.user.id, + teamId: null, + }, + }); + + // Patch response and get Responses endpoints right now work for teams only + // We need to fix them in a followup PR + teamData.routingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + ...routingFormData, + routes: [ { - id: "question2", - type: "text", - label: "Question 2", - required: false, - identifier: "question2", + ...routingFormData.routes[0], + action: { + type: "eventTypeRedirectUrl", + eventTypeId: orgAdminData.eventType.id, + value: `team/${teamData.team.slug}/${orgAdminData.eventType.slug}`, + }, }, + routingFormData.routes[1], ], - settings: { - emailOwnerOnSubmission: false, - }, - teamId: team.id, - userId: user.id, + userId: orgAdminData.user.id, + teamId: teamData.team.id, }, }); - routingFormResponse = await prismaWriteService.prisma.app_RoutingForms_FormResponse.create({ + teamData.routingFormResponse1 = await prismaWriteService.prisma.app_RoutingForms_FormResponse.create({ data: { - formId: routingForm.id, + formId: teamData.routingForm.id, response: JSON.stringify({ question1: "answer1", question2: "answer2" }), }, }); - routingFormResponse2 = await prismaWriteService.prisma.app_RoutingForms_FormResponse.create({ + teamData.routingFormResponse2 = await prismaWriteService.prisma.app_RoutingForms_FormResponse.create({ data: { - formId: routingForm.id, + formId: teamData.routingForm.id, response: { question1: "answer1", question2: "answer2" }, }, }); @@ -196,33 +294,33 @@ describe("OrganizationsRoutingFormsResponsesController", () => { describe(`GET /v2/organizations/:orgId/routing-forms/:routingFormId/responses`, () => { it("should not get routing form responses for non existing org", async () => { return request(app.getHttpServer()) - .get(`/v2/organizations/99999/routing-forms/${routingForm.id}/responses`) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .get(`/v2/organizations/99999/routing-forms/${teamData.routingForm.id}/responses`) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .expect(403); }); it("should not get routing form responses for non existing form", async () => { return request(app.getHttpServer()) .get(`/v2/organizations/${org.id}/routing-forms/non-existent-id/responses`) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .expect(404); }); it("should not get routing form responses without authentication", async () => { return request(app.getHttpServer()) - .get(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses`) + .get(`/v2/organizations/${org.id}/routing-forms/${teamData.routingForm.id}/responses`) .expect(401); }); it("should get routing form responses", async () => { - const createdAt = new Date(routingFormResponse.createdAt); + const createdAt = new Date(teamData.routingFormResponse1.createdAt); createdAt.setHours(createdAt.getHours() - 1); const isoStringCreatedAt = createdAt.toISOString(); return request(app.getHttpServer()) .get( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?skip=0&take=2&sortUpdatedAt=asc&sortCreatedAt=desc&afterCreatedAt=${isoStringCreatedAt}` + `/v2/organizations/${org.id}/routing-forms/${teamData.routingForm.id}/responses?skip=0&take=2&sortUpdatedAt=asc&sortCreatedAt=desc&afterCreatedAt=${isoStringCreatedAt}` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .expect(200) .then((response) => { const responseBody = response.body; @@ -230,14 +328,18 @@ describe("OrganizationsRoutingFormsResponsesController", () => { const responses = responseBody.data as App_RoutingForms_FormResponse[]; expect(responses).toBeDefined(); expect(responses.length).toBeGreaterThan(0); - expect(responses.find((response) => response.id === routingFormResponse.id)).toBeDefined(); - expect(responses.find((response) => response.id === routingFormResponse.id)?.formId).toEqual( - routingFormResponse.formId - ); - expect(responses.find((response) => response.id === routingFormResponse2.id)).toBeDefined(); - expect(responses.find((response) => response.id === routingFormResponse2.id)?.formId).toEqual( - routingFormResponse2.formId - ); + expect( + responses.find((response) => response.id === teamData.routingFormResponse1.id) + ).toBeDefined(); + expect( + responses.find((response) => response.id === teamData.routingFormResponse1.id)?.formId + ).toEqual(teamData.routingFormResponse1.formId); + expect( + responses.find((response) => response.id === teamData.routingFormResponse2.id) + ).toBeDefined(); + expect( + responses.find((response) => response.id === teamData.routingFormResponse2.id)?.formId + ).toEqual(teamData.routingFormResponse2.formId); }); }); }); @@ -246,9 +348,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 403 when organization does not exist", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/99999/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/99999/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", }) @@ -260,7 +362,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { .post( `/v2/organizations/${org.id}/routing-forms/non-existent-id/responses?start=2050-09-05&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", }) @@ -270,7 +372,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 401 when authentication token is missing", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) .send({ question1: "answer1", @@ -281,9 +383,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should create response and return available slots when routing to event type", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", // This matches the route condition question2: "answer2", @@ -296,7 +398,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { expect(data).toBeDefined(); expect(data.routing?.responseId).toBeDefined(); expect(typeof data.routing?.responseId).toBe("number"); - expect(data.eventTypeId).toEqual(routingEventType.id); + expect(data.eventTypeId).toEqual(orgAdminData.eventType.id); expect(data.slots).toBeDefined(); expect(typeof data.slots).toBe("object"); }); @@ -305,9 +407,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 400 when required form fields are missing", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question2: "answer2", // Missing required question1 }) @@ -317,9 +419,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should create response and return custom message if the routing is to custom page", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "different-answer", // This won't match any route question2: "answer2", @@ -338,8 +440,10 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 400 when required slot query parameters are missing", async () => { // Missing start parameter await request(app.getHttpServer()) - .post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?end=2050-09-06`) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .post( + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", question2: "answer2", @@ -348,8 +452,10 @@ describe("OrganizationsRoutingFormsResponsesController", () => { // Missing end parameter await request(app.getHttpServer()) - .post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05`) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .post( + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05` + ) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", question2: "answer2", @@ -360,9 +466,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 400 when date parameters have invalid format", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=invalid-date&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=invalid-date&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", }) @@ -372,9 +478,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 400 when end date is before start date", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-10&end=2050-09-05` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-10&end=2050-09-05` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", }) @@ -394,7 +500,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { const response = await request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) .set({ Authorization: `Bearer cal_test_${unauthorizedApiKey}` }) .send({ @@ -412,9 +518,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should handle queued response creation", async () => { return request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06&queueResponse=true` + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06&queueResponse=true` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ question1: "answer1", question2: "answer2", @@ -430,16 +536,93 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should return 500 when event type is not found", async () => { // Create a routing form with an invalid eventTypeId - const routingFormWithInvalidEventType = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + const orgAdminUserRoutingFormWithInvalidEventType = + await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + name: "Test Routing Form with Invalid Event Type", + description: "Test Description", + disabled: false, + routes: [ + { + id: "route-1", + queryValue: { + id: "route-1", + type: "group", + children1: { + "rule-1": { + type: "rule", + properties: { + field: "question1", + operator: "equal", + value: ["answer1"], + valueSrc: ["value"], + valueType: ["text"], + }, + }, + }, + }, + action: { + type: "eventTypeRedirectUrl", + eventTypeId: 99999, // Invalid event type ID + value: `team/${teamData.team.slug}/non-existent-event-type`, + }, + isFallback: false, + }, + { + id: "fallback-route", + action: { type: "customPageMessage", value: "Fallback Message" }, + isFallback: true, + queryValue: { id: "fallback-route", type: "group" }, + }, + ], + fields: [ + { + id: "question1", + type: "text", + label: "Question 1", + required: true, + identifier: "question1", + }, + ], + settings: { + emailOwnerOnSubmission: false, + }, + + teamId: null, + userId: orgAdminData.user.id, + }, + }); + + // Try to create a response for the form with invalid event type + const response = await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/routing-forms/${orgAdminUserRoutingFormWithInvalidEventType.id}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) + .send({ + question1: "answer1", + }); + + expect(response.status).toBe(500); + + // Clean up the form + await prismaWriteService.prisma.app_RoutingForms_Form.delete({ + where: { id: orgAdminUserRoutingFormWithInvalidEventType.id }, + }); + }); + + it("should return external redirect URL when routing to external URL", async () => { + // Create a routing form with external redirect action + const externalRoutingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ data: { - name: "Test Routing Form with Invalid Event Type", - description: "Test Description", + name: "Test External Routing Form", + description: "Test Description for External Redirect", disabled: false, routes: [ { - id: "route-1", + id: "external-route-1", queryValue: { - id: "route-1", + id: "external-route-1", type: "group", children1: { "rule-1": { @@ -447,7 +630,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { properties: { field: "question1", operator: "equal", - value: ["answer1"], + value: ["external"], valueSrc: ["value"], valueType: ["text"], }, @@ -455,9 +638,8 @@ describe("OrganizationsRoutingFormsResponsesController", () => { }, }, action: { - type: "eventTypeRedirectUrl", - eventTypeId: 99999, // Invalid event type ID - value: `team/${team.slug}/non-existent-event-type`, + type: "externalRedirectUrl", + value: "https://example.com/external-booking", }, isFallback: false, }, @@ -480,39 +662,60 @@ describe("OrganizationsRoutingFormsResponsesController", () => { settings: { emailOwnerOnSubmission: false, }, - teamId: team.id, - userId: user.id, + teamId: null, + userId: orgAdminData.user.id, }, }); - // Try to create a response for the form with invalid event type const response = await request(app.getHttpServer()) .post( - `/v2/organizations/${org.id}/routing-forms/${routingFormWithInvalidEventType.id}/responses?start=2050-09-05&end=2050-09-06` + `/v2/organizations/${org.id}/routing-forms/${externalRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ - question1: "answer1", - }); + question1: "external", // This matches the route condition for external redirect + }) + .expect(201); - expect(response.status).toBe(500); + const responseBody = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + const data = responseBody.data; + expect(data).toBeDefined(); + expect(data.routingExternalRedirectUrl).toBeDefined(); + expect(data.routingExternalRedirectUrl).toContain("https://example.com/external-booking"); + expect(data.routingExternalRedirectUrl).toContain("cal.action=externalRedirectUrl"); - // Clean up the form + // Verify that it doesn't contain event type routing data + expect(data.eventTypeId).toBeUndefined(); + expect(data.slots).toBeUndefined(); + expect(data.routing).toBeUndefined(); + expect(data.routingCustomMessage).toBeUndefined(); + + // Clean up the external routing form await prismaWriteService.prisma.app_RoutingForms_Form.delete({ - where: { id: routingFormWithInvalidEventType.id }, + where: { id: externalRoutingForm.id }, }); }); + }); - it("should handle routing with team member assignments", async () => { - // This test verifies that routing forms can handle team member assignments - // and that the routing returns the correct team member information + describe(`POST /v2/organizations/:orgId/routing-forms/:routingFormId/responses - restrictions check`, () => { + // Helper functions to centralize API version header setting + const createAuthenticatedRequest = (method: "get" | "post" | "patch", url: string, apiKey: string) => { return request(app.getHttpServer()) - .post( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06` - ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + [method](url) + .set({ + Authorization: `Bearer cal_test_${apiKey}`, + }); + }; + + it("should allow non-org-admin user to access their own user routing form", async () => { + return createAuthenticatedRequest( + "post", + `/v2/organizations/${org.id}/routing-forms/${nonOrgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06`, + nonOrgAdminData.apiKey + ) .send({ - question1: "answer1", // This matches the route condition + question1: "answer1", question2: "answer2", }) .expect(201) @@ -523,25 +726,29 @@ describe("OrganizationsRoutingFormsResponsesController", () => { expect(data).toBeDefined(); expect(data.routing?.responseId).toBeDefined(); expect(typeof data.routing?.responseId).toBe("number"); - expect(data.eventTypeId).toEqual(routingEventType.id); + expect(data.eventTypeId).toEqual(nonOrgAdminData.eventType.id); expect(data.slots).toBeDefined(); - // Team member assignments would be part of the routing data - // This test validates the basic routing functionality works + expect(typeof data.slots).toBe("object"); }); }); - it("should return external redirect URL when routing to external URL", async () => { - // Create a routing form with external redirect action - const externalRoutingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + it("should return 403 when non-org-admin user tries to access routing form they don't own", async () => { + // Create a second user + const otherUser = await userRepositoryFixture.create({ + email: `other-user-${randomString()}@api.com`, + }); + + // Create a routing form that belongs to the other user + const otherorgAdminUserRoutingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ data: { - name: "Test External Routing Form", - description: "Test Description for External Redirect", + name: "Other User's Routing Form", + description: "Test Description", disabled: false, routes: [ { - id: "external-route-1", + id: "route-1", queryValue: { - id: "external-route-1", + id: "route-1", type: "group", children1: { "rule-1": { @@ -549,7 +756,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { properties: { field: "question1", operator: "equal", - value: ["external"], + value: ["answer1"], valueSrc: ["value"], valueType: ["text"], }, @@ -557,17 +764,12 @@ describe("OrganizationsRoutingFormsResponsesController", () => { }, }, action: { - type: "externalRedirectUrl", - value: "https://example.com/external-booking", + type: "eventTypeRedirectUrl", + eventTypeId: orgAdminData.eventType.id, + value: `team/${teamData.team.slug}/${orgAdminData.eventType.slug}`, }, isFallback: false, }, - { - id: "fallback-route", - action: { type: "customPageMessage", value: "Fallback Message" }, - isFallback: true, - queryValue: { id: "fallback-route", type: "group" }, - }, ], fields: [ { @@ -581,38 +783,65 @@ describe("OrganizationsRoutingFormsResponsesController", () => { settings: { emailOwnerOnSubmission: false, }, - teamId: team.id, - userId: user.id, + // User Routing Form has teamId=null + teamId: null, + userId: otherUser.id, // This form belongs to otherUser, not the authenticated user }, }); - const response = await request(app.getHttpServer()) - .post( - `/v2/organizations/${org.id}/routing-forms/${externalRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06` - ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) - .send({ - question1: "external", // This matches the route condition for external redirect - }) - .expect(201); + // Try to access the routing form that belongs to the other user + const response = await createAuthenticatedRequest( + "post", + `/v2/organizations/${org.id}/routing-forms/${otherorgAdminUserRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06`, + nonOrgAdminData.apiKey + ).send({ + question1: "answer1", + }); - const responseBody = response.body; - expect(responseBody.status).toEqual(SUCCESS_STATUS); - const data = responseBody.data; - expect(data).toBeDefined(); - expect(data.routingExternalRedirectUrl).toBeDefined(); - expect(data.routingExternalRedirectUrl).toContain("https://example.com/external-booking"); - expect(data.routingExternalRedirectUrl).toContain("cal.action=externalRedirectUrl"); + expect(response.status).toBe(403); + expect(response.body.error.message).toContain( + `Routing Form with id=${otherorgAdminUserRoutingForm.id} is not a user Routing Form owned by user with id=${nonOrgAdminData.user.id}.` + ); - // Verify that it doesn't contain event type routing data - expect(data.eventTypeId).toBeUndefined(); - expect(data.slots).toBeUndefined(); - expect(data.routing).toBeUndefined(); - expect(data.routingCustomMessage).toBeUndefined(); + // Clean up + await prismaWriteService.prisma.app_RoutingForms_Form.delete({ + where: { id: otherorgAdminUserRoutingForm.id }, + }); + await prismaWriteService.prisma.user.delete({ + where: { id: otherUser.id }, + }); + }); - // Clean up the external routing form + it("should allow org admin to access any routing form using RolesGuard", async () => { + return createAuthenticatedRequest( + "post", + `/v2/organizations/${org.id}/routing-forms/${orgAdminData.routingForm.id}/responses?start=2050-09-05&end=2050-09-06`, + orgAdminData.apiKey + ) + .send({ + question1: "answer1", + question2: "answer2", + }) + .expect(201) + .then((response) => { + const responseBody = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + const data = responseBody.data; + expect(data).toBeDefined(); + expect(data.routing?.responseId).toBeDefined(); + expect(typeof data.routing?.responseId).toBe("number"); + }); + }); + + afterAll(async () => { + // Clean up user routing form await prismaWriteService.prisma.app_RoutingForms_Form.delete({ - where: { id: externalRoutingForm.id }, + where: { id: orgAdminData.routingForm.id }, + }); + + // Clean up non-org-admin user + await prismaWriteService.prisma.user.delete({ + where: { id: nonOrgAdminData.user.id }, }); }); }); @@ -620,8 +849,10 @@ describe("OrganizationsRoutingFormsResponsesController", () => { describe(`PATCH /v2/organizations/:orgId/routing-forms/:routingFormId/responses/:responseId`, () => { it("should not update routing form response for non existing org", async () => { return request(app.getHttpServer()) - .patch(`/v2/organizations/99999/routing-forms/${routingForm.id}/responses/${routingFormResponse.id}`) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .patch( + `/v2/organizations/99999/routing-forms/${teamData.routingForm.id}/responses/${teamData.routingFormResponse1.id}` + ) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ response: JSON.stringify({ question1: "updated_answer1" }) }) .expect(403); }); @@ -629,17 +860,17 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should not update routing form response for non existing form", async () => { return request(app.getHttpServer()) .patch( - `/v2/organizations/${org.id}/routing-forms/non-existent-id/responses/${routingFormResponse.id}` + `/v2/organizations/${org.id}/routing-forms/non-existent-id/responses/${teamData.routingFormResponse1.id}` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ response: JSON.stringify({ question1: "updated_answer1" }) }) .expect(404); }); it("should not update routing form response for non existing response", async () => { return request(app.getHttpServer()) - .patch(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses/99999`) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .patch(`/v2/organizations/${org.id}/routing-forms/${teamData.routingForm.id}/responses/99999`) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ response: JSON.stringify({ question1: "updated_answer1" }) }) .expect(404); }); @@ -647,7 +878,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { it("should not update routing form response without authentication", async () => { return request(app.getHttpServer()) .patch( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses/${routingFormResponse.id}` + `/v2/organizations/${org.id}/routing-forms/${teamData.routingForm.id}/responses/${teamData.routingFormResponse1.id}` ) .send({ response: JSON.stringify({ question1: "updated_answer1" }) }) .expect(401); @@ -657,9 +888,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => { const updatedResponse = { question1: "updated_answer1", question2: "updated_answer2" }; return request(app.getHttpServer()) .patch( - `/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses/${routingFormResponse.id}` + `/v2/organizations/${org.id}/routing-forms/${teamData.routingForm.id}/responses/${teamData.routingFormResponse1.id}` ) - .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .set({ Authorization: `Bearer cal_test_${orgAdminData.apiKey}` }) .send({ response: updatedResponse }) .expect(200) .then((response) => { @@ -667,8 +898,8 @@ describe("OrganizationsRoutingFormsResponsesController", () => { expect(responseBody.status).toEqual(SUCCESS_STATUS); const data = responseBody.data; expect(data).toBeDefined(); - expect(data.id).toEqual(routingFormResponse.id); - expect(data.formId).toEqual(routingFormResponse.formId); + expect(data.id).toEqual(teamData.routingFormResponse1.id); + expect(data.formId).toEqual(teamData.routingFormResponse1.formId); expect(data.response).toEqual(updatedResponse); }); }); @@ -677,12 +908,12 @@ describe("OrganizationsRoutingFormsResponsesController", () => { afterAll(async () => { await prismaWriteService.prisma.app_RoutingForms_FormResponse.delete({ where: { - id: routingFormResponse.id, + id: teamData.routingFormResponse1.id, }, }); await prismaWriteService.prisma.app_RoutingForms_FormResponse.delete({ where: { - id: routingFormResponse2.id, + id: teamData.routingFormResponse2.id, }, }); await prismaWriteService.prisma.app_RoutingForms_Form.deleteMany({ @@ -697,7 +928,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { }); await prismaWriteService.prisma.team.delete({ where: { - id: team.id, + id: teamData.team.id, }, }); await prismaWriteService.prisma.team.delete({ @@ -707,7 +938,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => { }); await prismaWriteService.prisma.user.delete({ where: { - id: user.id, + id: orgAdminData.user.id, }, }); diff --git a/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts b/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts index 2cef0243f1434d..fadb51dc11ea39 100644 --- a/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts +++ b/apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts @@ -4,8 +4,10 @@ import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.de import { Roles } from "@/modules/auth/decorators/roles/roles.decorator"; import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard"; import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard"; +import { Or } from "@/modules/auth/guards/or-guard"; import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard"; import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard"; +import { IsUserRoutingForm } from "@/modules/auth/guards/organizations/is-user-routing-form.guard"; import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; import { GetRoutingFormResponsesOutput } from "@/modules/organizations/routing-forms/outputs/get-routing-form-responses.output"; import { OrganizationsRoutingFormsResponsesService } from "@/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service"; @@ -20,12 +22,12 @@ import { UseGuards, ParseIntPipe, Req, + Version, } from "@nestjs/common"; import { ApiHeader, ApiOperation, ApiTags } from "@nestjs/swagger"; import { Request } from "express"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import { GetAvailableSlotsInput_2024_09_04 } from "@calcom/platform-types"; import { CreateRoutingFormResponseInput } from "../inputs/create-routing-form-response.input"; import { GetRoutingFormResponsesParams } from "../inputs/get-routing-form-responses-params.input"; @@ -37,7 +39,7 @@ import { UpdateRoutingFormResponseOutput } from "../outputs/update-routing-form- path: "/v2/organizations/:orgId/routing-forms/:routingFormId/responses", version: API_VERSIONS_VALUES, }) -@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard) +@UseGuards(ApiAuthGuard, IsOrgGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard) @ApiTags("Orgs / Routing forms") @ApiHeader(API_KEY_HEADER) export class OrganizationsRoutingFormsResponsesController { @@ -48,6 +50,7 @@ export class OrganizationsRoutingFormsResponsesController { @Get("/") @ApiOperation({ summary: "Get routing form responses" }) @Roles("ORG_ADMIN") + @UseGuards(RolesGuard) @PlatformPlan("ESSENTIALS") async getRoutingFormResponses( @Param("orgId", ParseIntPipe) orgId: number, @@ -74,6 +77,7 @@ export class OrganizationsRoutingFormsResponsesController { @Post("/") @ApiOperation({ summary: "Create routing form response and get available slots" }) @Roles("ORG_ADMIN") + @UseGuards(Or([RolesGuard, IsUserRoutingForm])) @PlatformPlan("ESSENTIALS") async createRoutingFormResponse( @Param("orgId", ParseIntPipe) orgId: number, @@ -82,7 +86,6 @@ export class OrganizationsRoutingFormsResponsesController { @Req() request: Request ): Promise { const result = await this.organizationsRoutingFormsResponsesService.createRoutingFormResponseWithSlots( - orgId, routingFormId, query, request @@ -97,6 +100,7 @@ export class OrganizationsRoutingFormsResponsesController { @Patch("/:responseId") @ApiOperation({ summary: "Update routing form response" }) @Roles("ORG_ADMIN") + @UseGuards(RolesGuard) @PlatformPlan("ESSENTIALS") async updateRoutingFormResponse( @Param("orgId", ParseIntPipe) orgId: number, diff --git a/apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts b/apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts index 023d40fe4cf668..d329b59b84128f 100644 --- a/apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts +++ b/apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts @@ -1,3 +1,5 @@ +import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository"; +import { IsUserRoutingForm } from "@/modules/auth/guards/organizations/is-user-routing-form.guard"; import { MembershipsRepository } from "@/modules/memberships/memberships.repository"; import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository"; import { OrganizationsTeamsRoutingFormsResponsesOutputService } from "@/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses-output.service"; @@ -14,17 +16,21 @@ import { OrganizationsRoutingFormsController } from "./controllers/organizations import { OrganizationsRoutingFormsRepository } from "./organizations-routing-forms.repository"; import { OrganizationsRoutingFormsResponsesService } from "./services/organizations-routing-forms-responses.service"; import { OrganizationsRoutingFormsService } from "./services/organizations-routing-forms.service"; +import { SharedRoutingFormResponseService } from "./services/shared-routing-form-response.service"; @Module({ imports: [PrismaModule, StripeModule, RedisModule, RoutingFormsModule, SlotsModule_2024_09_04], providers: [ + IsUserRoutingForm, MembershipsRepository, OrganizationsRepository, OrganizationsRoutingFormsRepository, OrganizationsRoutingFormsService, OrganizationsRoutingFormsResponsesService, + SharedRoutingFormResponseService, OrganizationsTeamsRoutingFormsResponsesOutputService, TeamsEventTypesRepository, + EventTypesRepository_2024_06_14, ], controllers: [OrganizationsRoutingFormsController, OrganizationsRoutingFormsResponsesController], }) diff --git a/apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts b/apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts index 3da86f0d81e6a8..2fb399c43aa75b 100644 --- a/apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts +++ b/apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts @@ -1,18 +1,9 @@ import { OrganizationsRoutingFormsRepository } from "@/modules/organizations/routing-forms/organizations-routing-forms.repository"; +import { SharedRoutingFormResponseService } from "@/modules/organizations/routing-forms/services/shared-routing-form-response.service"; import { OrganizationsTeamsRoutingFormsResponsesOutputService } from "@/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses-output.service"; -import { SlotsService_2024_09_04 } from "@/modules/slots/slots-2024-09-04/services/slots.service"; -import { TeamsEventTypesRepository } from "@/modules/teams/event-types/teams-event-types.repository"; -import { - BadRequestException, - Injectable, - NotFoundException, - InternalServerErrorException, -} from "@nestjs/common"; +import { Injectable } from "@nestjs/common"; import { Request } from "express"; -import { getRoutedUrl } from "@calcom/platform-libraries"; -import { ById_2024_09_04_type } from "@calcom/platform-types"; - import type { CreateRoutingFormResponseInput } from "../inputs/create-routing-form-response.input"; import type { CreateRoutingFormResponseOutputData } from "../outputs/create-routing-form-response.output"; @@ -21,8 +12,7 @@ export class OrganizationsRoutingFormsResponsesService { constructor( private readonly organizationsRoutingFormsRepository: OrganizationsRoutingFormsRepository, private readonly outputService: OrganizationsTeamsRoutingFormsResponsesOutputService, - private readonly slotsService: SlotsService_2024_09_04, - private readonly teamsEventTypesRepository: TeamsEventTypesRepository + private readonly sharedRoutingFormResponseService: SharedRoutingFormResponseService ) {} async getOrganizationRoutingFormResponses( @@ -51,6 +41,18 @@ export class OrganizationsRoutingFormsResponsesService { return this.outputService.getRoutingFormResponses(responses); } + async createRoutingFormResponseWithSlots( + routingFormId: string, + query: CreateRoutingFormResponseInput, + request: Request + ): Promise { + return this.sharedRoutingFormResponseService.createRoutingFormResponseWithSlots( + routingFormId, + query, + request + ); + } + async updateRoutingFormResponse( orgId: number, routingFormId: string, @@ -68,201 +70,4 @@ export class OrganizationsRoutingFormsResponsesService { return this.outputService.getRoutingFormResponses([updatedResponse])[0]; } - - async createRoutingFormResponseWithSlots( - orgId: number, - routingFormId: string, - query: CreateRoutingFormResponseInput, - request: Request - ): Promise { - const { queueResponse, ...slotsQuery } = query; - - this.validateDateRange(slotsQuery.start, slotsQuery.end); - - const { redirectUrl, customMessage } = await this.getRoutingUrl( - request, - routingFormId, - queueResponse ?? false - ); - - // If there is no redirect URL, then we have to show the message as that would be custom page message to be shown as per the route chosen - if (!redirectUrl) { - return { - routingCustomMessage: customMessage, - }; - } - - if (!this.isEventTypeRedirectUrl(redirectUrl)) { - return { - routingExternalRedirectUrl: redirectUrl.toString(), - }; - } - - // Extract event type information from the routed URL - const { eventTypeId, crmParams } = await this.extractEventTypeAndCrmParams(redirectUrl); - - const paramsForGetAvailableSlots = { - type: ById_2024_09_04_type, - eventTypeId, - ...slotsQuery, - ...crmParams, - } as const; - - // Get available slots using the slots service with CRM parameters - const slots = await this.slotsService.getAvailableSlots(paramsForGetAvailableSlots); - const teamMemberIds = crmParams.routedTeamMemberIds ?? []; - const teamMemberEmail = crmParams.teamMemberEmail ?? undefined; - const skipContactOwner = crmParams.skipContactOwner ?? undefined; - const queuedResponseId = crmParams.queuedFormResponseId ?? null; - const responseId = crmParams.routingFormResponseId ?? null; - const crmAppSlug = crmParams.crmAppSlug ?? undefined; - const crmOwnerRecordType = crmParams.crmOwnerRecordType ?? undefined; - - if (responseId) { - return { - routing: { - responseId, - teamMemberEmail, - teamMemberIds, - skipContactOwner, - crmAppSlug, - crmOwnerRecordType, - }, - eventTypeId, - slots, - }; - } - - if (!queuedResponseId) { - throw new InternalServerErrorException( - "No routing form response ID or queued form response ID could be found." - ); - } - - return { - routing: { - queuedResponseId, - teamMemberEmail, - teamMemberIds, - skipContactOwner, - crmAppSlug, - crmOwnerRecordType, - }, - eventTypeId, - slots, - }; - } - - private validateDateRange(start: string, end: string) { - const startDate = new Date(start); - const endDate = new Date(end); - - if (endDate < startDate) { - throw new BadRequestException("End date cannot be before start date."); - } - } - - private async getRoutingUrl(request: Request, formId: string, queueResponse: boolean) { - const params = Object.fromEntries(new URLSearchParams(request.body)); - const routedUrlData = await getRoutedUrl( - { - req: request, - query: { ...params, form: formId, ...(queueResponse && { "cal.queueFormResponse": "true" }) }, - }, - true - ); - - if (routedUrlData.notFound) { - throw new NotFoundException("Routing form not found."); - } - - if (routedUrlData?.props?.errorMessage) { - throw new BadRequestException(routedUrlData.props.errorMessage); - } - - const destination = routedUrlData?.redirect?.destination; - - if (!destination) { - if (routedUrlData?.props?.message) { - return { - redirectUrl: null, - customMessage: routedUrlData.props.message, - }; - } - // This should never happen because there is always a fallback route - throw new InternalServerErrorException("No route found."); - } - - return { - redirectUrl: new URL(destination), - customMessage: null, - }; - } - - private async extractEventTypeAndCrmParams(routingUrl: URL) { - // Extract team and event type information - const { teamId, eventTypeSlug } = this.extractTeamIdAndEventTypeSlugFromRedirectUrl(routingUrl); - const eventType = await this.teamsEventTypesRepository.getEventTypeByTeamIdAndSlug(teamId, eventTypeSlug); - - if (!eventType?.id) { - // This could only happen if the event-type earlier selected as route action was deleted - throw new InternalServerErrorException("Chosen event type not found."); - } - - // Extract CRM parameters from URL - const urlParams = routingUrl.searchParams; - const crmParams = { - teamMemberEmail: urlParams.get("cal.crmContactOwnerEmail") || undefined, - routedTeamMemberIds: urlParams.get("cal.routedTeamMemberIds") - ? urlParams - .get("cal.routedTeamMemberIds")! - .split(",") - .map((id) => parseInt(id)) - : undefined, - routingFormResponseId: urlParams.get("cal.routingFormResponseId") - ? parseInt(urlParams.get("cal.routingFormResponseId")!) - : undefined, - queuedFormResponseId: urlParams.get("cal.queuedFormResponseId") - ? (urlParams.get("cal.queuedFormResponseId") as string) - : undefined, - skipContactOwner: urlParams.get("cal.skipContactOwner") === "true" ? true : false, - crmAppSlug: urlParams.get("cal.crmAppSlug") || undefined, - crmOwnerRecordType: urlParams.get("cal.crmContactOwnerRecordType") || undefined, - }; - - return { - eventTypeId: eventType.id, - crmParams, - }; - } - - private isEventTypeRedirectUrl(routingUrl: URL) { - const routingSearchParams = routingUrl.searchParams; - return routingSearchParams.get("cal.action") === "eventTypeRedirectUrl"; - } - - private extractTeamIdAndEventTypeSlugFromRedirectUrl(routingUrl: URL) { - const eventTypeSlug = this.extractEventTypeFromRoutedUrl(routingUrl); - const teamId = this.extractTeamIdFromRoutedUrl(routingUrl); - - if (!teamId) { - throw new NotFoundException("Team ID not found in the routed URL."); - } - - if (!eventTypeSlug) { - throw new NotFoundException("Event type slug not found in the routed URL."); - } - - return { teamId, eventTypeSlug }; - } - - private extractTeamIdFromRoutedUrl(routingUrl: URL) { - const routingSearchParams = routingUrl.searchParams; - return Number(routingSearchParams.get("cal.teamId")); - } - - private extractEventTypeFromRoutedUrl(routingUrl: URL) { - const pathNameParams = routingUrl.pathname.split("/"); - return pathNameParams[pathNameParams.length - 1]; - } } diff --git a/apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts b/apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts new file mode 100644 index 00000000000000..d19e5666ae9601 --- /dev/null +++ b/apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts @@ -0,0 +1,227 @@ +import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository"; +import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy"; +import { CreateRoutingFormResponseInput } from "@/modules/organizations/routing-forms/inputs/create-routing-form-response.input"; +import { CreateRoutingFormResponseOutputData } from "@/modules/organizations/routing-forms/outputs/create-routing-form-response.output"; +import { SlotsService_2024_09_04 } from "@/modules/slots/slots-2024-09-04/services/slots.service"; +import { TeamsEventTypesRepository } from "@/modules/teams/event-types/teams-event-types.repository"; +import { + Injectable, + BadRequestException, + InternalServerErrorException, + NotFoundException, +} from "@nestjs/common"; +import { Request } from "express"; + +import { getRoutedUrl } from "@calcom/platform-libraries"; +import { ById_2024_09_04_type } from "@calcom/platform-types"; + +@Injectable() +export class SharedRoutingFormResponseService { + constructor( + private readonly slotsService: SlotsService_2024_09_04, + private readonly teamsEventTypesRepository: TeamsEventTypesRepository, + private readonly eventTypesRepository: EventTypesRepository_2024_06_14 + ) {} + + async createRoutingFormResponseWithSlots( + routingFormId: string, + query: CreateRoutingFormResponseInput, + request: Request + ): Promise { + const { queueResponse, ...slotsQuery } = query; + const user = request.user as ApiAuthGuardUser; + + this.validateDateRange(slotsQuery.start, slotsQuery.end); + + const { redirectUrl, customMessage } = await this.getRoutingUrl( + request, + routingFormId, + queueResponse ?? false + ); + + // If there is no redirect URL, then we have to show the message as that would be custom page message to be shown as per the route chosen + if (!redirectUrl) { + return { + routingCustomMessage: customMessage, + }; + } + + if (!this.isEventTypeRedirectUrl(redirectUrl)) { + return { + routingExternalRedirectUrl: redirectUrl.toString(), + }; + } + + // Extract event type information from the routed URL + const { eventTypeId, crmParams } = await this.extractEventTypeAndCrmParams(user.id, redirectUrl); + + const paramsForGetAvailableSlots = { + type: ById_2024_09_04_type, + eventTypeId, + ...slotsQuery, + ...crmParams, + } as const; + + // Get available slots using the slots service with CRM parameters + const slots = await this.slotsService.getAvailableSlots(paramsForGetAvailableSlots); + const teamMemberIds = crmParams.routedTeamMemberIds ?? []; + const teamMemberEmail = crmParams.teamMemberEmail ?? undefined; + const skipContactOwner = crmParams.skipContactOwner ?? undefined; + const queuedResponseId = crmParams.queuedFormResponseId ?? null; + const responseId = crmParams.routingFormResponseId ?? null; + const crmAppSlug = crmParams.crmAppSlug ?? undefined; + const crmOwnerRecordType = crmParams.crmOwnerRecordType ?? undefined; + + if (responseId) { + return { + routing: { + responseId, + teamMemberEmail, + teamMemberIds, + skipContactOwner, + crmAppSlug, + crmOwnerRecordType, + }, + eventTypeId, + slots, + }; + } + + if (!queuedResponseId) { + throw new InternalServerErrorException( + "No routing form response ID or queued form response ID could be found." + ); + } + + return { + routing: { + queuedResponseId, + teamMemberEmail, + teamMemberIds, + skipContactOwner, + crmAppSlug, + crmOwnerRecordType, + }, + eventTypeId, + slots, + }; + } + + private validateDateRange(start: string, end: string) { + const startDate = new Date(start); + const endDate = new Date(end); + + if (endDate < startDate) { + throw new BadRequestException("End date cannot be before start date."); + } + } + + private async getRoutingUrl(request: Request, formId: string, queueResponse: boolean) { + const params = Object.fromEntries(new URLSearchParams(request.body)); + const routedUrlData = await getRoutedUrl( + { + req: request, + query: { ...params, form: formId, ...(queueResponse && { "cal.queueFormResponse": "true" }) }, + }, + true + ); + + if (routedUrlData.notFound) { + throw new NotFoundException("Routing form not found."); + } + + if (routedUrlData?.props?.errorMessage) { + throw new BadRequestException(routedUrlData.props.errorMessage); + } + + const destination = routedUrlData?.redirect?.destination; + + if (!destination) { + if (routedUrlData?.props?.message) { + return { + redirectUrl: null, + customMessage: routedUrlData.props.message, + }; + } + // This should never happen because there is always a fallback route + throw new InternalServerErrorException("No route found."); + } + + return { + redirectUrl: new URL(destination), + customMessage: null, + }; + } + + private async extractEventTypeAndCrmParams(userId: number, routingUrl: URL) { + // Extract team and event type information + // TODO: Route action also has eventTypeId directly now and instead of using this brittle approach for getting event type by slug, we should get by eventTypeId + const { teamId, eventTypeSlug } = this.extractTeamIdAndEventTypeSlugFromRedirectUrl(routingUrl); + const eventType = teamId + ? await this.teamsEventTypesRepository.getEventTypeByTeamIdAndSlug(teamId, eventTypeSlug) + : await this.eventTypesRepository.getUserEventTypeBySlug(userId, eventTypeSlug); + + if (!eventType?.id) { + // This could only happen if the event-type earlier selected as route action was deleted + throw new InternalServerErrorException( + `Chosen event type identified by slug ${eventTypeSlug} not found.` + ); + } + + // Extract CRM parameters from URL + const urlParams = routingUrl.searchParams; + const crmParams = { + teamMemberEmail: urlParams.get("cal.crmContactOwnerEmail") || undefined, + routedTeamMemberIds: urlParams.get("cal.routedTeamMemberIds") + ? urlParams + .get("cal.routedTeamMemberIds")! + .split(",") + .map((id) => parseInt(id)) + : undefined, + routingFormResponseId: urlParams.get("cal.routingFormResponseId") + ? parseInt(urlParams.get("cal.routingFormResponseId")!) + : undefined, + queuedFormResponseId: urlParams.get("cal.queuedFormResponseId") + ? (urlParams.get("cal.queuedFormResponseId") as string) + : undefined, + skipContactOwner: urlParams.get("cal.skipContactOwner") === "true" ? true : false, + crmAppSlug: urlParams.get("cal.crmAppSlug") || undefined, + crmOwnerRecordType: urlParams.get("cal.crmContactOwnerRecordType") || undefined, + }; + + return { + eventTypeId: eventType.id, + crmParams, + }; + } + + private isEventTypeRedirectUrl(routingUrl: URL) { + const routingSearchParams = routingUrl.searchParams; + return routingSearchParams.get("cal.action") === "eventTypeRedirectUrl"; + } + + private extractTeamIdAndEventTypeSlugFromRedirectUrl(routingUrl: URL) { + const eventTypeSlug = this.extractEventTypeFromRoutedUrl(routingUrl); + const teamId = this.extractTeamIdFromRoutedUrl(routingUrl); + + if (!eventTypeSlug) { + throw new InternalServerErrorException("Event type slug not found in the routed URL."); + } + + return { teamId, eventTypeSlug }; + } + + private extractTeamIdFromRoutedUrl(routingUrl: URL) { + const routingSearchParams = routingUrl.searchParams; + const teamId = Number(routingSearchParams.get("cal.teamId")); + if (isNaN(teamId)) { + return null; + } + return teamId; + } + + private extractEventTypeFromRoutedUrl(routingUrl: URL) { + const pathNameParams = routingUrl.pathname.split("/"); + return pathNameParams[pathNameParams.length - 1]; + } +} diff --git a/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts b/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts index 8e5e5c9d74c169..0a43113b245c77 100644 --- a/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts +++ b/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts @@ -1,6 +1,7 @@ import { bootstrap } from "@/app"; import { AppModule } from "@/app.module"; import { GetRoutingFormResponsesOutput } from "@/modules/organizations/teams/routing-forms/outputs/get-routing-form-responses.output"; +import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { PrismaModule } from "@/modules/prisma/prisma.module"; import { TokensModule } from "@/modules/tokens/tokens.module"; import { UsersModule } from "@/modules/users/users.module"; @@ -23,6 +24,7 @@ import { Team } from "@calcom/prisma/client"; describe("Organizations Teams Routing Forms Responses", () => { let app: INestApplication; + let prismaWriteService: PrismaWriteService; let userRepositoryFixture: UserRepositoryFixture; let organizationsRepositoryFixture: OrganizationRepositoryFixture; @@ -41,6 +43,13 @@ describe("Organizations Teams Routing Forms Responses", () => { let apiKeyString: string; let routingFormId: string; + let routingEventType: { + id: number; + slug: string | null; + teamId: number | null; + userId: number | null; + title: string; + }; const routingFormResponses = [ { formFillerId: `${randomString()}`, @@ -59,6 +68,7 @@ describe("Organizations Teams Routing Forms Responses", () => { imports: [AppModule, PrismaModule, UsersModule, TokensModule], }).compile(); + prismaWriteService = moduleRef.get(PrismaWriteService); userRepositoryFixture = new UserRepositoryFixture(moduleRef); organizationsRepositoryFixture = new OrganizationRepositoryFixture(moduleRef); teamsRepositoryFixture = new TeamRepositoryFixture(moduleRef); @@ -98,6 +108,17 @@ describe("Organizations Teams Routing Forms Responses", () => { team: { connect: { id: orgTeam.id } }, }); + // Create an event type for routing form to route to + routingEventType = await prismaWriteService.prisma.eventType.create({ + data: { + title: "Test Event Type", + slug: "test-event-type", + length: 30, + userId: user.id, + teamId: orgTeam.id, + }, + }); + await profileRepositoryFixture.create({ uid: `usr-${user.id}`, username: authEmail, @@ -118,18 +139,59 @@ describe("Organizations Teams Routing Forms Responses", () => { description: null, position: 0, disabled: false, - fields: JSON.stringify([ + fields: [ { + id: "participant-field", type: "text", label: "participant", required: true, + identifier: "participant", + }, + { + id: "question2-field", + type: "text", + label: "question2", + required: false, + identifier: "question2", }, - ]), - routes: JSON.stringify([ + ], + routes: [ { + id: "route-1", + queryValue: { + id: "route-1", + type: "group", + children1: { + "rule-1": { + type: "rule", + properties: { + field: "question1", + operator: "equal", + value: ["answer1"], + valueSrc: ["value"], + valueType: ["text"], + }, + }, + }, + }, + action: { + type: "eventTypeRedirectUrl", + eventTypeId: routingEventType.id, + value: `team/${orgTeam.slug}/${routingEventType.slug}`, + }, + isFallback: false, + }, + { + id: "fallback-route", + queryValue: { + id: "fallback-route", + type: "group", + children1: {}, + }, action: { type: "customPageMessage", value: "Thank you for your response" }, + isFallback: true, }, - ]), + ], user: { connect: { id: user.id, @@ -190,6 +252,455 @@ describe("Organizations Teams Routing Forms Responses", () => { }); }); + describe(`POST /v2/organizations/:orgId/teams/:teamId/routing-forms/:routingFormId/responses`, () => { + describe("permissions", () => { + it("should return 403 when organization does not exist", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/99999/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "answer1", + }) + .expect(403); + }); + + it("should return 404 when team does not exist", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/99999/routing-forms/${routingFormId}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "answer1", + }) + .expect(404) + .then((response) => { + expect(response.body.error.message).toContain(`IsTeamInOrg - Team (99999) not found.`); + }); + }); + + it("should return 403 when routing form does not exist in the team", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/non-existent-id/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "answer1", + }) + .expect(403) + .then((response) => { + expect(response.body.error.message).toContain( + `IsRoutingFormInTeam - team with id=(${orgTeam.id}) does not own routing form with id=(non-existent-id).` + ); + }); + }); + + it("should return 403 when routing form belongs to different team", async () => { + // Create a second team within the same organization + const otherTeam = await teamsRepositoryFixture.create({ + name: `other-team-${randomString()}`, + isOrganization: false, + parent: { connect: { id: org.id } }, + }); + + // Create a routing form that belongs to the other team + const otherTeamRoutingForm = await routingFormsRepositoryFixture.create({ + name: "Other Team's Routing Form", + description: "Test Description", + position: 0, + disabled: false, + fields: [ + { + type: "text", + label: "Question 1", + required: true, + }, + ], + routes: [ + { + action: { type: "customPageMessage", value: "Thank you for your response" }, + }, + ], + user: { + connect: { + id: user.id, + }, + }, + team: { + connect: { + id: otherTeam.id, + }, + }, + }); + + // Try to access the routing form that belongs to the other team + const response = await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${otherTeamRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "answer1", + }); + + expect(response.status).toBe(403); + expect(response.body.error.message).toContain( + `IsRoutingFormInTeam - team with id=(${orgTeam.id}) does not own routing form with id=(${otherTeamRoutingForm.id}).` + ); + + // Clean up + await routingFormsRepositoryFixture.delete(otherTeamRoutingForm.id); + await teamsRepositoryFixture.delete(otherTeam.id); + }); + + it("should return 401 when authentication token is missing", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=2050-09-05&end=2050-09-06` + ) + .send({ + question1: "answer1", + }) + .expect(401); + }); + }); + + it("should return 400 when required form fields are missing", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + // Missing required participant field + }) + .expect(400); + }); + + it("should return 400 when required slot query parameters are missing", async () => { + // Missing start parameter + await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + participant: "test-participant", + }) + .expect(400); + + // Missing end parameter + await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=2050-09-05` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + participant: "test-participant", + }) + .expect(400); + }); + + it("should return 400 when date parameters have invalid format", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=invalid-date&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + participant: "test-participant", + }) + .expect(400); + }); + + it("should return 400 when end date is before start date", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=2050-09-10&end=2050-09-05` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + participant: "test-participant", + }) + .expect(400); + }); + + it("should handle queued response creation", async () => { + return request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormId}/responses?start=2050-09-05&end=2050-09-06&queueResponse=true` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + participant: "test-participant", + }) + .expect(201) + .then((response) => { + const responseBody = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + const data = responseBody.data; + console.log("responseBody", responseBody.data); + expect(data.routing?.queuedResponseId).toBeDefined(); + }); + }); + + it("should create response and return available slots when routing to event type", async () => { + // Create a routing form with event type routing + const eventTypeRoutingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + name: "Test Event Type Routing Form", + description: "Test Description", + disabled: false, + routes: [ + { + id: "route-1", + queryValue: { + id: "route-1", + type: "group", + children1: { + "rule-1": { + type: "rule", + properties: { + field: "question1", + operator: "equal", + value: ["answer1"], + valueSrc: ["value"], + valueType: ["text"], + }, + }, + }, + }, + action: { + type: "eventTypeRedirectUrl", + eventTypeId: routingEventType.id, + value: `team/${orgTeam.slug}/${routingEventType.slug}`, + }, + isFallback: false, + }, + { + id: "fallback-route", + action: { type: "customPageMessage", value: "Fallback Message" }, + isFallback: true, + queryValue: { id: "fallback-route", type: "group" }, + }, + ], + fields: [ + { + id: "question1", + type: "text", + label: "Question 1", + required: true, + identifier: "question1", + }, + { + id: "question2", + type: "text", + label: "Question 2", + required: false, + identifier: "question2", + }, + ], + settings: { + emailOwnerOnSubmission: false, + }, + teamId: orgTeam.id, + userId: user.id, + }, + }); + + const response = await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${eventTypeRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "answer1", // This matches the route condition + question2: "answer2", + }) + .expect(201); + + const responseBody = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + const data = responseBody.data; + expect(data).toBeDefined(); + expect(data.routing?.responseId).toBeDefined(); + expect(typeof data.routing?.responseId).toBe("number"); + expect(data.eventTypeId).toEqual(routingEventType.id); + expect(data.slots).toBeDefined(); + expect(typeof data.slots).toBe("object"); + expect(data.routing?.teamMemberIds).toBeDefined(); + + // Clean up + await prismaWriteService.prisma.app_RoutingForms_Form.delete({ + where: { id: eventTypeRoutingForm.id }, + }); + }); + + it("should return 500 when event type is not found", async () => { + // Create a routing form with an invalid eventTypeId + const routingFormWithInvalidEventType = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + name: "Test Routing Form with Invalid Event Type", + description: "Test Description", + disabled: false, + routes: [ + { + id: "route-1", + queryValue: { + id: "route-1", + type: "group", + children1: { + "rule-1": { + type: "rule", + properties: { + field: "question1", + operator: "equal", + value: ["answer1"], + valueSrc: ["value"], + valueType: ["text"], + }, + }, + }, + }, + action: { + type: "eventTypeRedirectUrl", + eventTypeId: 99999, // Invalid event type ID + value: `team/${orgTeam.slug}/non-existent-event-type`, + }, + isFallback: false, + }, + { + id: "fallback-route", + action: { type: "customPageMessage", value: "Fallback Message" }, + isFallback: true, + queryValue: { id: "fallback-route", type: "group" }, + }, + ], + fields: [ + { + id: "question1", + type: "text", + label: "Question 1", + required: true, + identifier: "question1", + }, + ], + settings: { + emailOwnerOnSubmission: false, + }, + teamId: orgTeam.id, + userId: user.id, + }, + }); + + // Try to create a response for the form with invalid event type + const response = await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${routingFormWithInvalidEventType.id}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "answer1", + }); + + expect(response.status).toBe(500); + + // Clean up the form + await prismaWriteService.prisma.app_RoutingForms_Form.delete({ + where: { id: routingFormWithInvalidEventType.id }, + }); + }); + + it("should return external redirect URL when routing to external URL", async () => { + // Create a routing form with external redirect action + const externalRoutingForm = await prismaWriteService.prisma.app_RoutingForms_Form.create({ + data: { + name: "Test External Routing Form", + description: "Test Description for External Redirect", + disabled: false, + routes: [ + { + id: "external-route-1", + queryValue: { + id: "external-route-1", + type: "group", + children1: { + "rule-1": { + type: "rule", + properties: { + field: "question1", + operator: "equal", + value: ["external"], + valueSrc: ["value"], + valueType: ["text"], + }, + }, + }, + }, + action: { + type: "externalRedirectUrl", + value: "https://example.com/external-booking", + }, + isFallback: false, + }, + { + id: "fallback-route", + action: { type: "customPageMessage", value: "Fallback Message" }, + isFallback: true, + queryValue: { id: "fallback-route", type: "group" }, + }, + ], + fields: [ + { + id: "question1", + type: "text", + label: "Question 1", + required: true, + identifier: "question1", + }, + ], + settings: { + emailOwnerOnSubmission: false, + }, + teamId: orgTeam.id, + userId: user.id, + }, + }); + + const response = await request(app.getHttpServer()) + .post( + `/v2/organizations/${org.id}/teams/${orgTeam.id}/routing-forms/${externalRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06` + ) + .set({ Authorization: `Bearer cal_test_${apiKeyString}` }) + .send({ + question1: "external", // This matches the route condition for external redirect + }) + .expect(201); + + const responseBody = response.body; + expect(responseBody.status).toEqual(SUCCESS_STATUS); + const data = responseBody.data; + expect(data).toBeDefined(); + expect(data.routingExternalRedirectUrl).toBeDefined(); + expect(data.routingExternalRedirectUrl).toContain("https://example.com/external-booking"); + expect(data.routingExternalRedirectUrl).toContain("cal.action=externalRedirectUrl"); + + // Verify that it doesn't contain event type routing data + expect(data.eventTypeId).toBeUndefined(); + expect(data.slots).toBeUndefined(); + expect(data.routing).toBeUndefined(); + expect(data.routingCustomMessage).toBeUndefined(); + + // Clean up the external routing form + await prismaWriteService.prisma.app_RoutingForms_Form.delete({ + where: { id: externalRoutingForm.id }, + }); + }); + }); + describe(`PATCH /v2/organizations/:orgId/routing-forms/:routingFormId/responses/:responseId`, () => { it("should not update routing form response for non existing org", async () => { return request(app.getHttpServer()) @@ -246,8 +757,13 @@ describe("Organizations Teams Routing Forms Responses", () => { }); afterAll(async () => { - await routingFormsRepositoryFixture.deleteResponse(routingFormResponseId); + if (routingFormResponseId) { + await routingFormsRepositoryFixture.deleteResponse(routingFormResponseId); + } await routingFormsRepositoryFixture.delete(routingFormId); + await prismaWriteService.prisma.eventType.delete({ + where: { id: routingEventType.id }, + }); await userRepositoryFixture.deleteByEmail(user.email); await organizationsRepositoryFixture.delete(org.id); await app.close(); diff --git a/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts b/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts index 22ed0bd9cd2389..5727fb163cd15b 100644 --- a/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts +++ b/apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts @@ -9,13 +9,27 @@ import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard"; import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; import { IsRoutingFormInTeam } from "@/modules/auth/guards/routing-forms/is-routing-form-in-team.guard"; import { IsTeamInOrg } from "@/modules/auth/guards/teams/is-team-in-org.guard"; +import { CreateRoutingFormResponseInput } from "@/modules/organizations/routing-forms/inputs/create-routing-form-response.input"; import { GetRoutingFormResponsesParams } from "@/modules/organizations/routing-forms/inputs/get-routing-form-responses-params.input"; import { UpdateRoutingFormResponseInput } from "@/modules/organizations/routing-forms/inputs/update-routing-form-response.input"; +import { CreateRoutingFormResponseOutput } from "@/modules/organizations/routing-forms/outputs/create-routing-form-response.output"; import { UpdateRoutingFormResponseOutput } from "@/modules/organizations/routing-forms/outputs/update-routing-form-response.output"; import { GetRoutingFormResponsesOutput } from "@/modules/organizations/teams/routing-forms/outputs/get-routing-form-responses.output"; import { OrganizationsTeamsRoutingFormsResponsesService } from "@/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service"; -import { Controller, Get, Patch, Param, ParseIntPipe, Query, UseGuards, Body } from "@nestjs/common"; +import { + Controller, + Get, + Post, + Patch, + Param, + ParseIntPipe, + Query, + UseGuards, + Body, + Req, +} from "@nestjs/common"; import { ApiHeader, ApiOperation, ApiTags } from "@nestjs/swagger"; +import { Request } from "express"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; @@ -66,6 +80,30 @@ export class OrganizationsTeamsRoutingFormsResponsesController { }; } + @Post("/") + @ApiOperation({ summary: "Create routing form response and get available slots" }) + @Roles("TEAM_MEMBER") + @PlatformPlan("ESSENTIALS") + async createRoutingFormResponse( + @Param("orgId", ParseIntPipe) orgId: number, + @Param("teamId", ParseIntPipe) teamId: number, + @Param("routingFormId") routingFormId: string, + @Query() query: CreateRoutingFormResponseInput, + @Req() request: Request + ): Promise { + const result = + await this.organizationsTeamsRoutingFormsResponsesService.createRoutingFormResponseWithSlots( + routingFormId, + query, + request + ); + + return { + status: SUCCESS_STATUS, + data: result, + }; + } + @Patch("/:responseId") @ApiOperation({ summary: "Update routing form response" }) @Roles("TEAM_ADMIN") diff --git a/apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts b/apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts index 4d46f91ef0b86b..c1527ee6cd9e33 100644 --- a/apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts +++ b/apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts @@ -1,7 +1,9 @@ +import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository"; import { MembershipsRepository } from "@/modules/memberships/memberships.repository"; import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository"; import { OrganizationsRoutingFormsRepository } from "@/modules/organizations/routing-forms/organizations-routing-forms.repository"; import { OrganizationsRoutingFormsResponsesService } from "@/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service"; +import { SharedRoutingFormResponseService } from "@/modules/organizations/routing-forms/services/shared-routing-form-response.service"; import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository"; import { PrismaModule } from "@/modules/prisma/prisma.module"; import { RedisModule } from "@/modules/redis/redis.module"; @@ -31,6 +33,7 @@ import { OrganizationsTeamsRoutingFormsService } from "./services/organizations- providers: [ OrganizationsTeamsRoutingFormsService, OrganizationsTeamsRoutingFormsResponsesService, + SharedRoutingFormResponseService, OrganizationsTeamsRoutingFormsResponsesOutputService, OrganizationsTeamsRoutingFormsResponsesRepository, OrganizationsTeamsRoutingFormsRepository, @@ -39,6 +42,7 @@ import { OrganizationsTeamsRoutingFormsService } from "./services/organizations- MembershipsRepository, OrganizationsRoutingFormsResponsesService, OrganizationsRoutingFormsRepository, + EventTypesRepository_2024_06_14, ], controllers: [OrganizationsTeamsRoutingFormsResponsesController, OrganizationsTeamsRoutingFormsController], }) diff --git a/apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts b/apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts index aa57d287baab20..2d9644ca348a1a 100644 --- a/apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts +++ b/apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts @@ -1,5 +1,9 @@ +import { CreateRoutingFormResponseInput } from "@/modules/organizations/routing-forms/inputs/create-routing-form-response.input"; +import { CreateRoutingFormResponseOutputData } from "@/modules/organizations/routing-forms/outputs/create-routing-form-response.output"; +import { SharedRoutingFormResponseService } from "@/modules/organizations/routing-forms/services/shared-routing-form-response.service"; import { OrganizationsTeamsRoutingFormsResponsesOutputService } from "@/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses-output.service"; import { Injectable } from "@nestjs/common"; +import { Request } from "express"; import { OrganizationsTeamsRoutingFormsResponsesRepository } from "../repositories/organizations-teams-routing-forms-responses.repository"; @@ -7,7 +11,8 @@ import { OrganizationsTeamsRoutingFormsResponsesRepository } from "../repositori export class OrganizationsTeamsRoutingFormsResponsesService { constructor( private readonly routingFormsRepository: OrganizationsTeamsRoutingFormsResponsesRepository, - private readonly routingFormsResponsesOutputService: OrganizationsTeamsRoutingFormsResponsesOutputService + private readonly routingFormsResponsesOutputService: OrganizationsTeamsRoutingFormsResponsesOutputService, + private readonly sharedRoutingFormResponseService: SharedRoutingFormResponseService ) {} async getTeamRoutingFormResponses( @@ -36,6 +41,18 @@ export class OrganizationsTeamsRoutingFormsResponsesService { return this.routingFormsResponsesOutputService.getRoutingFormResponses(responses); } + async createRoutingFormResponseWithSlots( + routingFormId: string, + query: CreateRoutingFormResponseInput, + request: Request + ): Promise { + return this.sharedRoutingFormResponseService.createRoutingFormResponseWithSlots( + routingFormId, + query, + request + ); + } + async updateTeamRoutingFormResponse( teamId: number, routingFormId: string, diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index 246ce4ab0c6d11..1dadbea1ef5398 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -5065,6 +5065,134 @@ "tags": [ "Orgs / Teams / Routing forms / Responses" ] + }, + "post": { + "operationId": "OrganizationsTeamsRoutingFormsResponsesController_createRoutingFormResponse", + "summary": "Create routing form response and get available slots", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` is api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "orgId", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "teamId", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "routingFormId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + }, + { + "name": "start", + "required": true, + "in": "query", + "description": "\n Time starting from which available slots should be checked.\n \n Must be in UTC timezone as ISO 8601 datestring.\n \n You can pass date without hours which defaults to start of day or specify hours:\n 2024-08-13 (will have hours 00:00:00 aka at very beginning of the date) or you can specify hours manually like 2024-08-13T09:00:00Z\n ", + "example": "2050-09-05", + "schema": { + "type": "string" + } + }, + { + "name": "end", + "required": true, + "in": "query", + "description": "\n Time until which available slots should be checked.\n \n Must be in UTC timezone as ISO 8601 datestring.\n \n You can pass date without hours which defaults to end of day or specify hours:\n 2024-08-20 (will have hours 23:59:59 aka at the very end of the date) or you can specify hours manually like 2024-08-20T18:00:00Z", + "example": "2050-09-06", + "schema": { + "type": "string" + } + }, + { + "name": "timeZone", + "required": false, + "in": "query", + "description": "Time zone in which the available slots should be returned. Defaults to UTC.", + "example": "Europe/Rome", + "schema": { + "type": "string" + } + }, + { + "name": "duration", + "required": false, + "in": "query", + "description": "If event type has multiple possible durations then you can specify the desired duration here. Also, if you are fetching slots for a dynamic event then you can specify the duration her which defaults to 30, meaning that returned slots will be each 30 minutes long.", + "example": "60", + "schema": { + "type": "number" + } + }, + { + "name": "format", + "required": false, + "in": "query", + "description": "Format of slot times in response. Use 'range' to get start and end times.", + "example": "range", + "schema": { + "enum": [ + "range", + "time" + ], + "type": "string" + } + }, + { + "name": "bookingUidToReschedule", + "required": false, + "in": "query", + "description": "The unique identifier of the booking being rescheduled. When provided will ensure that the original booking time appears within the returned available slots when rescheduling.", + "example": "abc123def456", + "schema": { + "type": "string" + } + }, + { + "name": "queueResponse", + "required": false, + "in": "query", + "description": "Whether to queue the form response.", + "example": true, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRoutingFormResponseOutput" + } + } + } + } + }, + "tags": [ + "Orgs / Teams / Routing forms / Responses" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/routing-forms/{routingFormId}/responses/{responseId}": { @@ -17627,7 +17755,7 @@ }, "bookingFields": { "type": "array", - "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "description": "Complete set of booking form fields. This array replaces all existing booking fields. To modify existing fields, first fetch the current event type, then include all desired fields in this array. Sending only one field will remove all other custom fields, keeping only default fields plus the provided one.", "items": { "oneOf": [ { @@ -20335,7 +20463,7 @@ }, "bookingFields": { "type": "array", - "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "description": "Complete set of booking form fields. This array replaces all existing booking fields. To modify existing fields, first fetch the current event type, then include all desired fields in this array. Sending only one field will remove all other custom fields, keeping only default fields plus the provided one.", "items": { "oneOf": [ { @@ -24822,6 +24950,126 @@ "data" ] }, + "Routing": { + "type": "object", + "properties": { + "queuedResponseId": { + "type": "string", + "nullable": true, + "description": "The ID of the queued form response. Only present if the form response was queued.", + "example": "123" + }, + "responseId": { + "type": "number", + "nullable": true, + "description": "The ID of the routing form response.", + "example": 123 + }, + "teamMemberIds": { + "description": "Array of team member IDs that were routed to handle this booking.", + "example": [ + 101, + 102 + ], + "type": "array", + "items": { + "type": "number" + } + }, + "teamMemberEmail": { + "type": "string", + "description": "The email of the team member assigned to handle this booking.", + "example": "john.doe@example.com" + }, + "skipContactOwner": { + "type": "boolean", + "description": "Whether to skip contact owner assignment from CRM integration.", + "example": true + }, + "crmAppSlug": { + "type": "string", + "description": "The CRM application slug for integration.", + "example": "salesforce" + }, + "crmOwnerRecordType": { + "type": "string", + "description": "The CRM owner record type for contact assignment.", + "example": "Account" + } + }, + "required": [ + "teamMemberIds" + ] + }, + "CreateRoutingFormResponseOutputData": { + "type": "object", + "properties": { + "eventTypeId": { + "type": "number", + "description": "The ID of the event type that was routed to.", + "example": 123 + }, + "routing": { + "description": "The routing information.", + "example": { + "eventTypeId": 123, + "routing": { + "teamMemberIds": [ + 101, + 102 + ], + "teamMemberEmail": "john.doe@example.com", + "skipContactOwner": true + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/Routing" + } + ] + }, + "routingCustomMessage": { + "type": "string", + "description": "A custom message to be displayed to the user in case of routing to a custom page.", + "example": "This is a custom message." + }, + "routingExternalRedirectUrl": { + "type": "string", + "description": "The external redirect URL to be used in case of routing to a non cal.com event type URL.", + "example": "https://example.com/" + }, + "slots": { + "oneOf": [ + { + "$ref": "#/components/schemas/SlotsOutput_2024_09_04" + }, + { + "$ref": "#/components/schemas/RangeSlotsOutput_2024_09_04" + } + ] + } + } + }, + "CreateRoutingFormResponseOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "$ref": "#/components/schemas/CreateRoutingFormResponseOutputData" + } + }, + "required": [ + "status", + "data" + ] + }, "UpdateRoutingFormResponseInput": { "type": "object", "properties": { @@ -25453,51 +25701,6 @@ "timeZone" ] }, - "Routing": { - "type": "object", - "properties": { - "responseId": { - "type": "number", - "description": "The ID of the routing form response that determined this booking assignment.", - "example": 123 - }, - "teamMemberIds": { - "description": "Array of team member IDs that were routed to handle this booking.", - "example": [ - 101, - 102 - ], - "type": "array", - "items": { - "type": "number" - } - }, - "teamMemberEmail": { - "type": "string", - "description": "The email of the team member assigned to handle this booking.", - "example": "john.doe@example.com" - }, - "skipContactOwner": { - "type": "boolean", - "description": "Whether to skip contact owner assignment from CRM integration.", - "example": true - }, - "crmAppSlug": { - "type": "string", - "description": "The CRM application slug for integration.", - "example": "salesforce" - }, - "crmOwnerRecordType": { - "type": "string", - "description": "The CRM owner record type for contact assignment.", - "example": "Account" - } - }, - "required": [ - "responseId", - "teamMemberIds" - ] - }, "CreateBookingInput_2024_08_13": { "type": "object", "properties": { @@ -28344,75 +28547,6 @@ "data" ] }, - "CreateRoutingFormResponseOutputData": { - "type": "object", - "properties": { - "eventTypeId": { - "type": "number", - "description": "The ID of the event type that was routed to.", - "example": 123 - }, - "routing": { - "description": "The routing information.", - "example": { - "eventTypeId": 123, - "routing": { - "teamMemberIds": [ - 101, - 102 - ], - "teamMemberEmail": "john.doe@example.com", - "skipContactOwner": true - } - }, - "allOf": [ - { - "$ref": "#/components/schemas/Routing" - } - ] - }, - "routingCustomMessage": { - "type": "string", - "description": "A custom message to be displayed to the user in case of routing to a custom page.", - "example": "This is a custom message." - }, - "routingExternalRedirectUrl": { - "type": "string", - "description": "The external redirect URL to be used in case of routing to a non cal.com event type URL.", - "example": "https://example.com/" - }, - "slots": { - "oneOf": [ - { - "$ref": "#/components/schemas/SlotsOutput_2024_09_04" - }, - { - "$ref": "#/components/schemas/RangeSlotsOutput_2024_09_04" - } - ] - } - } - }, - "CreateRoutingFormResponseOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": [ - "success", - "error" - ] - }, - "data": { - "$ref": "#/components/schemas/CreateRoutingFormResponseOutputData" - } - }, - "required": [ - "status", - "data" - ] - }, "RequestEmailVerificationInput": { "type": "object", "properties": { diff --git a/docs/api-reference/v2/openapi.json b/docs/api-reference/v2/openapi.json index ac9487ae7fba3f..1dadbea1ef5398 100644 --- a/docs/api-reference/v2/openapi.json +++ b/docs/api-reference/v2/openapi.json @@ -69,7 +69,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] }, "post": { "operationId": "OAuthClientUsersController_createUser", @@ -115,7 +117,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] } }, "/v2/oauth-clients/{clientId}/users/{userId}": { @@ -161,7 +165,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] }, "patch": { "operationId": "OAuthClientUsersController_updateUser", @@ -215,7 +221,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] }, "delete": { "operationId": "OAuthClientUsersController_deleteUser", @@ -259,7 +267,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] } }, "/v2/oauth-clients/{clientId}/users/{userId}/force-refresh": { @@ -306,7 +316,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] } }, "/v2/oauth/{clientId}/refresh": { @@ -355,7 +367,9 @@ } } }, - "tags": ["Platform / Managed Users"] + "tags": [ + "Platform / Managed Users" + ] } }, "/v2/oauth-clients/{clientId}/webhooks": { @@ -403,7 +417,9 @@ } } }, - "tags": ["Platform / Webhooks"] + "tags": [ + "Platform / Webhooks" + ] }, "get": { "operationId": "OAuthClientWebhooksController_getOAuthClientWebhooks", @@ -464,7 +480,9 @@ } } }, - "tags": ["Platform / Webhooks"] + "tags": [ + "Platform / Webhooks" + ] }, "delete": { "operationId": "OAuthClientWebhooksController_deleteAllOAuthClientWebhooks", @@ -500,7 +518,9 @@ } } }, - "tags": ["Platform / Webhooks"] + "tags": [ + "Platform / Webhooks" + ] } }, "/v2/oauth-clients/{clientId}/webhooks/{webhookId}": { @@ -548,7 +568,9 @@ } } }, - "tags": ["Platform / Webhooks"] + "tags": [ + "Platform / Webhooks" + ] }, "get": { "operationId": "OAuthClientWebhooksController_getOAuthClientWebhook", @@ -576,7 +598,9 @@ } } }, - "tags": ["Platform / Webhooks"] + "tags": [ + "Platform / Webhooks" + ] }, "delete": { "operationId": "OAuthClientWebhooksController_deleteOAuthClientWebhook", @@ -604,7 +628,9 @@ } } }, - "tags": ["Platform / Webhooks"] + "tags": [ + "Platform / Webhooks" + ] } }, "/v2/organizations/{orgId}/attributes": { @@ -667,7 +693,9 @@ } } }, - "tags": ["Orgs / Attributes"] + "tags": [ + "Orgs / Attributes" + ] }, "post": { "operationId": "OrganizationsAttributesController_createOrganizationAttribute", @@ -713,7 +741,9 @@ } } }, - "tags": ["Orgs / Attributes"] + "tags": [ + "Orgs / Attributes" + ] } }, "/v2/organizations/{orgId}/attributes/{attributeId}": { @@ -759,7 +789,9 @@ } } }, - "tags": ["Orgs / Attributes"] + "tags": [ + "Orgs / Attributes" + ] }, "patch": { "operationId": "OrganizationsAttributesController_updateOrganizationAttribute", @@ -813,7 +845,9 @@ } } }, - "tags": ["Orgs / Attributes"] + "tags": [ + "Orgs / Attributes" + ] }, "delete": { "operationId": "OrganizationsAttributesController_deleteOrganizationAttribute", @@ -857,7 +891,9 @@ } } }, - "tags": ["Orgs / Attributes"] + "tags": [ + "Orgs / Attributes" + ] } }, "/v2/organizations/{orgId}/attributes/{attributeId}/options": { @@ -913,7 +949,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] }, "get": { "operationId": "OrganizationsAttributesOptionsController_getOrganizationAttributeOptions", @@ -957,7 +995,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] } }, "/v2/organizations/{orgId}/attributes/{attributeId}/options/{optionId}": { @@ -1011,7 +1051,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] }, "patch": { "operationId": "OrganizationsAttributesOptionsController_updateOrganizationAttributeOption", @@ -1073,7 +1115,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] } }, "/v2/organizations/{orgId}/attributes/{attributeId}/options/assigned": { @@ -1163,7 +1207,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] } }, "/v2/organizations/{orgId}/attributes/slugs/{attributeSlug}/options/assigned": { @@ -1253,7 +1299,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] } }, "/v2/organizations/{orgId}/attributes/options/{userId}": { @@ -1309,7 +1357,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] }, "get": { "operationId": "OrganizationsAttributesOptionsController_getOrganizationAttributeOptionsForUser", @@ -1353,7 +1403,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] } }, "/v2/organizations/{orgId}/attributes/options/{userId}/{attributeOptionId}": { @@ -1407,7 +1459,9 @@ } } }, - "tags": ["Orgs / Attributes / Options"] + "tags": [ + "Orgs / Attributes / Options" + ] } }, "/v2/organizations/{orgId}/bookings": { @@ -1452,7 +1506,13 @@ "type": "array", "items": { "type": "string", - "enum": ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] + "enum": [ + "upcoming", + "recurring", + "past", + "cancelled", + "unconfirmed" + ] } } }, @@ -1593,7 +1653,10 @@ "description": "Sort results by their start time in ascending or descending order.", "example": "?sortStart=asc OR ?sortStart=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -1604,7 +1667,10 @@ "description": "Sort results by their end time in ascending or descending order.", "example": "?sortEnd=asc OR ?sortEnd=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -1615,7 +1681,10 @@ "description": "Sort results by their creation time (when booking was made) in ascending or descending order.", "example": "?sortCreated=asc OR ?sortCreated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -1626,7 +1695,10 @@ "description": "Sort results by their updated time (for example when booking status changes) in ascending or descending order.", "example": "?sortUpdated=asc OR ?sortUpdated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -1683,7 +1755,9 @@ } } }, - "tags": ["Orgs / Bookings"] + "tags": [ + "Orgs / Bookings" + ] } }, "/v2/organizations/{orgId}/delegation-credentials": { @@ -1749,7 +1823,9 @@ } } }, - "tags": ["Orgs / Delegation Credentials"] + "tags": [ + "Orgs / Delegation Credentials" + ] } }, "/v2/organizations/{orgId}/delegation-credentials/{credentialId}": { @@ -1823,7 +1899,9 @@ } } }, - "tags": ["Orgs / Delegation Credentials"] + "tags": [ + "Orgs / Delegation Credentials" + ] } }, "/v2/organizations/{orgId}/memberships": { @@ -1904,7 +1982,9 @@ } } }, - "tags": ["Orgs / Memberships"] + "tags": [ + "Orgs / Memberships" + ] }, "post": { "operationId": "OrganizationsMembershipsController_createMembership", @@ -1968,7 +2048,9 @@ } } }, - "tags": ["Orgs / Memberships"] + "tags": [ + "Orgs / Memberships" + ] } }, "/v2/organizations/{orgId}/memberships/{membershipId}": { @@ -2032,7 +2114,9 @@ } } }, - "tags": ["Orgs / Memberships"] + "tags": [ + "Orgs / Memberships" + ] }, "delete": { "operationId": "OrganizationsMembershipsController_deleteMembership", @@ -2094,7 +2178,9 @@ } } }, - "tags": ["Orgs / Memberships"] + "tags": [ + "Orgs / Memberships" + ] }, "patch": { "operationId": "OrganizationsMembershipsController_updateMembership", @@ -2166,7 +2252,9 @@ } } }, - "tags": ["Orgs / Memberships"] + "tags": [ + "Orgs / Memberships" + ] } }, "/v2/organizations/{orgId}/routing-forms": { @@ -2215,7 +2303,10 @@ "in": "query", "description": "Sort by creation time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -2225,7 +2316,10 @@ "in": "query", "description": "Sort by update time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -2304,7 +2398,9 @@ } } }, - "tags": ["Orgs / Routing forms"] + "tags": [ + "Orgs / Routing forms" + ] } }, "/v2/organizations/{orgId}/routing-forms/{routingFormId}/responses": { @@ -2361,7 +2457,10 @@ "in": "query", "description": "Sort by creation time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -2371,7 +2470,10 @@ "in": "query", "description": "Sort by update time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -2437,7 +2539,9 @@ } } }, - "tags": ["Orgs / Routing forms"] + "tags": [ + "Orgs / Routing forms" + ] }, "post": { "operationId": "OrganizationsRoutingFormsResponsesController_createRoutingFormResponse", @@ -2515,7 +2619,10 @@ "description": "Format of slot times in response. Use 'range' to get start and end times.", "example": "range", "schema": { - "enum": ["range", "time"], + "enum": [ + "range", + "time" + ], "type": "string" } }, @@ -2552,7 +2659,9 @@ } } }, - "tags": ["Orgs / Routing forms"] + "tags": [ + "Orgs / Routing forms" + ] } }, "/v2/organizations/{orgId}/routing-forms/{routingFormId}/responses/{responseId}": { @@ -2616,7 +2725,9 @@ } } }, - "tags": ["Orgs / Routing forms"] + "tags": [ + "Orgs / Routing forms" + ] } }, "/v2/organizations/{orgId}/schedules": { @@ -2697,7 +2808,9 @@ } } }, - "tags": ["Orgs / Schedules"] + "tags": [ + "Orgs / Schedules" + ] } }, "/v2/organizations/{orgId}/teams": { @@ -2778,7 +2891,9 @@ } } }, - "tags": ["Orgs / Teams"] + "tags": [ + "Orgs / Teams" + ] }, "post": { "operationId": "OrganizationsTeamsController_createTeam", @@ -2842,7 +2957,9 @@ } } }, - "tags": ["Orgs / Teams"] + "tags": [ + "Orgs / Teams" + ] } }, "/v2/organizations/{orgId}/teams/me": { @@ -2923,7 +3040,9 @@ } } }, - "tags": ["Orgs / Teams"] + "tags": [ + "Orgs / Teams" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}": { @@ -2971,7 +3090,9 @@ } } }, - "tags": ["Orgs / Teams"] + "tags": [ + "Orgs / Teams" + ] }, "delete": { "operationId": "OrganizationsTeamsController_deleteTeam", @@ -3033,7 +3154,9 @@ } } }, - "tags": ["Orgs / Teams"] + "tags": [ + "Orgs / Teams" + ] }, "patch": { "operationId": "OrganizationsTeamsController_updateTeam", @@ -3105,7 +3228,9 @@ } } }, - "tags": ["Orgs / Teams"] + "tags": [ + "Orgs / Teams" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/bookings": { @@ -3150,7 +3275,13 @@ "type": "array", "items": { "type": "string", - "enum": ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] + "enum": [ + "upcoming", + "recurring", + "past", + "cancelled", + "unconfirmed" + ] } } }, @@ -3221,7 +3352,10 @@ "description": "Sort results by their start time in ascending or descending order.", "example": "?sortStart=asc OR ?sortStart=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -3232,7 +3366,10 @@ "description": "Sort results by their end time in ascending or descending order.", "example": "?sortEnd=asc OR ?sortEnd=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -3243,7 +3380,10 @@ "description": "Sort results by their creation time (when booking was made) in ascending or descending order.", "example": "?sortCreated=asc OR ?sortCreated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -3299,7 +3439,9 @@ } } }, - "tags": ["Orgs / Teams / Bookings"] + "tags": [ + "Orgs / Teams / Bookings" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/bookings/{bookingUid}/references": { @@ -3373,7 +3515,9 @@ } } }, - "tags": ["Orgs / Teams / Bookings"] + "tags": [ + "Orgs / Teams / Bookings" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing/{app}/connect": { @@ -3403,7 +3547,9 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet"], + "enum": [ + "google-meet" + ], "type": "string" } } @@ -3420,7 +3566,9 @@ } } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing/{app}/oauth/auth-url": { @@ -3458,7 +3606,10 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["zoom", "msteams"], + "enum": [ + "zoom", + "msteams" + ], "type": "string" } }, @@ -3491,7 +3642,9 @@ } } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing": { @@ -3520,7 +3673,9 @@ } } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing/{app}/default": { @@ -3542,7 +3697,12 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet", "zoom", "msteams", "daily-video"], + "enum": [ + "google-meet", + "zoom", + "msteams", + "daily-video" + ], "type": "string" } } @@ -3559,7 +3719,9 @@ } } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing/default": { @@ -3581,7 +3743,12 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet", "zoom", "msteams", "daily-video"], + "enum": [ + "google-meet", + "zoom", + "msteams", + "daily-video" + ], "type": "string" } } @@ -3598,7 +3765,9 @@ } } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing/{app}/disconnect": { @@ -3620,7 +3789,11 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet", "zoom", "msteams"], + "enum": [ + "google-meet", + "zoom", + "msteams" + ], "type": "string" } } @@ -3637,7 +3810,9 @@ } } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/conferencing/{app}/oauth/callback": { @@ -3691,7 +3866,9 @@ "description": "" } }, - "tags": ["Orgs / Teams / Conferencing"] + "tags": [ + "Orgs / Teams / Conferencing" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/event-types": { @@ -3765,7 +3942,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] }, "get": { "operationId": "OrganizationsEventTypesController_getTeamEventTypes", @@ -3837,7 +4016,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/event-types/{eventTypeId}": { @@ -3901,7 +4082,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] }, "patch": { "operationId": "OrganizationsEventTypesController_updateTeamEventType", @@ -3973,7 +4156,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] }, "delete": { "operationId": "OrganizationsEventTypesController_deleteTeamEventType", @@ -4035,7 +4220,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/event-types/{eventTypeId}/create-phone-call": { @@ -4109,7 +4296,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] } }, "/v2/organizations/{orgId}/teams/event-types": { @@ -4190,7 +4379,9 @@ } } }, - "tags": ["Orgs / Teams / Event Types"] + "tags": [ + "Orgs / Teams / Event Types" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/memberships": { @@ -4279,7 +4470,9 @@ } } }, - "tags": ["Orgs / Teams / Memberships"] + "tags": [ + "Orgs / Teams / Memberships" + ] }, "post": { "operationId": "OrganizationsTeamsMembershipsController_createOrgTeamMembership", @@ -4351,7 +4544,9 @@ } } }, - "tags": ["Orgs / Teams / Memberships"] + "tags": [ + "Orgs / Teams / Memberships" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/memberships/{membershipId}": { @@ -4423,7 +4618,9 @@ } } }, - "tags": ["Orgs / Teams / Memberships"] + "tags": [ + "Orgs / Teams / Memberships" + ] }, "delete": { "operationId": "OrganizationsTeamsMembershipsController_deleteOrgTeamMembership", @@ -4493,7 +4690,9 @@ } } }, - "tags": ["Orgs / Teams / Memberships"] + "tags": [ + "Orgs / Teams / Memberships" + ] }, "patch": { "operationId": "OrganizationsTeamsMembershipsController_updateOrgTeamMembership", @@ -4573,7 +4772,9 @@ } } }, - "tags": ["Orgs / Teams / Memberships"] + "tags": [ + "Orgs / Teams / Memberships" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/routing-forms": { @@ -4630,7 +4831,10 @@ "in": "query", "description": "Sort by creation time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -4640,7 +4844,10 @@ "in": "query", "description": "Sort by update time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -4706,7 +4913,9 @@ } } }, - "tags": ["Orgs / Teams / Routing forms"] + "tags": [ + "Orgs / Teams / Routing forms" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/routing-forms/{routingFormId}/responses": { @@ -4771,7 +4980,10 @@ "in": "query", "description": "Sort by creation time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -4781,7 +4993,10 @@ "in": "query", "description": "Sort by update time", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -4847,7 +5062,137 @@ } } }, - "tags": ["Orgs / Teams / Routing forms / Responses"] + "tags": [ + "Orgs / Teams / Routing forms / Responses" + ] + }, + "post": { + "operationId": "OrganizationsTeamsRoutingFormsResponsesController_createRoutingFormResponse", + "summary": "Create routing form response and get available slots", + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` is api key prefixed with cal_", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "orgId", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "teamId", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, + { + "name": "routingFormId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + }, + { + "name": "start", + "required": true, + "in": "query", + "description": "\n Time starting from which available slots should be checked.\n \n Must be in UTC timezone as ISO 8601 datestring.\n \n You can pass date without hours which defaults to start of day or specify hours:\n 2024-08-13 (will have hours 00:00:00 aka at very beginning of the date) or you can specify hours manually like 2024-08-13T09:00:00Z\n ", + "example": "2050-09-05", + "schema": { + "type": "string" + } + }, + { + "name": "end", + "required": true, + "in": "query", + "description": "\n Time until which available slots should be checked.\n \n Must be in UTC timezone as ISO 8601 datestring.\n \n You can pass date without hours which defaults to end of day or specify hours:\n 2024-08-20 (will have hours 23:59:59 aka at the very end of the date) or you can specify hours manually like 2024-08-20T18:00:00Z", + "example": "2050-09-06", + "schema": { + "type": "string" + } + }, + { + "name": "timeZone", + "required": false, + "in": "query", + "description": "Time zone in which the available slots should be returned. Defaults to UTC.", + "example": "Europe/Rome", + "schema": { + "type": "string" + } + }, + { + "name": "duration", + "required": false, + "in": "query", + "description": "If event type has multiple possible durations then you can specify the desired duration here. Also, if you are fetching slots for a dynamic event then you can specify the duration her which defaults to 30, meaning that returned slots will be each 30 minutes long.", + "example": "60", + "schema": { + "type": "number" + } + }, + { + "name": "format", + "required": false, + "in": "query", + "description": "Format of slot times in response. Use 'range' to get start and end times.", + "example": "range", + "schema": { + "enum": [ + "range", + "time" + ], + "type": "string" + } + }, + { + "name": "bookingUidToReschedule", + "required": false, + "in": "query", + "description": "The unique identifier of the booking being rescheduled. When provided will ensure that the original booking time appears within the returned available slots when rescheduling.", + "example": "abc123def456", + "schema": { + "type": "string" + } + }, + { + "name": "queueResponse", + "required": false, + "in": "query", + "description": "Whether to queue the form response.", + "example": true, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateRoutingFormResponseOutput" + } + } + } + } + }, + "tags": [ + "Orgs / Teams / Routing forms / Responses" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/routing-forms/{routingFormId}/responses/{responseId}": { @@ -4911,7 +5256,9 @@ } } }, - "tags": ["Orgs / Teams / Routing forms / Responses"] + "tags": [ + "Orgs / Teams / Routing forms / Responses" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/stripe/connect": { @@ -4972,7 +5319,9 @@ } } }, - "tags": ["Orgs / Teams / Stripe"] + "tags": [ + "Orgs / Teams / Stripe" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/stripe/check": { @@ -5001,7 +5350,9 @@ } } }, - "tags": ["Orgs / Teams / Stripe"] + "tags": [ + "Orgs / Teams / Stripe" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/stripe/save": { @@ -5046,7 +5397,9 @@ } } }, - "tags": ["Orgs / Teams / Stripe"] + "tags": [ + "Orgs / Teams / Stripe" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/users/{userId}/schedules": { @@ -5102,7 +5455,9 @@ } } }, - "tags": ["Orgs / Teams / Users / Schedules"] + "tags": [ + "Orgs / Teams / Users / Schedules" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/workflows": { @@ -5191,7 +5546,9 @@ } } }, - "tags": ["Orgs / Teams / Workflows"] + "tags": [ + "Orgs / Teams / Workflows" + ] }, "post": { "operationId": "OrganizationTeamWorkflowsController_createWorkflow", @@ -5255,7 +5612,9 @@ } } }, - "tags": ["Orgs / Teams / Workflows"] + "tags": [ + "Orgs / Teams / Workflows" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/workflows/{workflowId}": { @@ -5319,7 +5678,9 @@ } } }, - "tags": ["Orgs / Teams / Workflows"] + "tags": [ + "Orgs / Teams / Workflows" + ] }, "patch": { "operationId": "OrganizationTeamWorkflowsController_updateWorkflow", @@ -5391,7 +5752,9 @@ } } }, - "tags": ["Orgs / Teams / Workflows"] + "tags": [ + "Orgs / Teams / Workflows" + ] }, "delete": { "operationId": "OrganizationTeamWorkflowsController_deleteWorkflow", @@ -5446,7 +5809,9 @@ "description": "" } }, - "tags": ["Orgs / Teams / Workflows"] + "tags": [ + "Orgs / Teams / Workflows" + ] } }, "/v2/organizations/{orgId}/users": { @@ -5545,7 +5910,11 @@ "example": "NONE", "schema": { "default": "AND", - "enum": ["OR", "AND", "NONE"], + "enum": [ + "OR", + "AND", + "NONE" + ], "type": "string" } }, @@ -5575,7 +5944,9 @@ } } }, - "tags": ["Orgs / Users"] + "tags": [ + "Orgs / Users" + ] }, "post": { "operationId": "OrganizationsUsersController_createOrganizationUser", @@ -5631,7 +6002,9 @@ } } }, - "tags": ["Orgs / Users"] + "tags": [ + "Orgs / Users" + ] } }, "/v2/organizations/{orgId}/users/{userId}": { @@ -5705,7 +6078,9 @@ } } }, - "tags": ["Orgs / Users"] + "tags": [ + "Orgs / Users" + ] }, "delete": { "operationId": "OrganizationsUsersController_deleteOrganizationUser", @@ -5767,7 +6142,9 @@ } } }, - "tags": ["Orgs / Users"] + "tags": [ + "Orgs / Users" + ] } }, "/v2/organizations/{orgId}/users/{userId}/bookings": { @@ -5828,7 +6205,13 @@ "type": "array", "items": { "type": "string", - "enum": ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] + "enum": [ + "upcoming", + "recurring", + "past", + "cancelled", + "unconfirmed" + ] } } }, @@ -5969,7 +6352,10 @@ "description": "Sort results by their start time in ascending or descending order.", "example": "?sortStart=asc OR ?sortStart=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -5980,8 +6366,11 @@ "description": "Sort results by their end time in ascending or descending order.", "example": "?sortEnd=asc OR ?sortEnd=desc", "schema": { - "enum": ["asc", "desc"], - "type": "string" + "enum": [ + "asc", + "desc" + ], + "type": "string" } }, { @@ -5991,7 +6380,10 @@ "description": "Sort results by their creation time (when booking was made) in ascending or descending order.", "example": "?sortCreated=asc OR ?sortCreated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -6002,7 +6394,10 @@ "description": "Sort results by their updated time (for example when booking status changes) in ascending or descending order.", "example": "?sortUpdated=asc OR ?sortUpdated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -6034,7 +6429,9 @@ "description": "" } }, - "tags": ["Orgs / Users / Bookings"] + "tags": [ + "Orgs / Users / Bookings" + ] } }, "/v2/organizations/{orgId}/users/{userId}/ooo": { @@ -6109,7 +6506,10 @@ "description": "Sort results by their start time in ascending or descending order.", "example": "?sortStart=asc OR ?sortStart=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -6120,7 +6520,10 @@ "description": "Sort results by their end time in ascending or descending order.", "example": "?sortEnd=asc OR ?sortEnd=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } } @@ -6130,7 +6533,9 @@ "description": "" } }, - "tags": ["Orgs / Users / OOO"] + "tags": [ + "Orgs / Users / OOO" + ] }, "post": { "operationId": "OrganizationsUsersOOOController_createOrganizationUserOOO", @@ -6187,7 +6592,9 @@ "description": "" } }, - "tags": ["Orgs / Users / OOO"] + "tags": [ + "Orgs / Users / OOO" + ] } }, "/v2/organizations/{orgId}/users/{userId}/ooo/{oooId}": { @@ -6254,7 +6661,9 @@ "description": "" } }, - "tags": ["Orgs / Users / OOO"] + "tags": [ + "Orgs / Users / OOO" + ] }, "delete": { "operationId": "OrganizationsUsersOOOController_deleteOrganizationUserOOO", @@ -6301,7 +6710,9 @@ "description": "" } }, - "tags": ["Orgs / Users / OOO"] + "tags": [ + "Orgs / Users / OOO" + ] } }, "/v2/organizations/{orgId}/ooo": { @@ -6376,7 +6787,10 @@ "description": "Sort results by their start time in ascending or descending order.", "example": "?sortStart=asc OR ?sortStart=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -6387,7 +6801,10 @@ "description": "Sort results by their end time in ascending or descending order.", "example": "?sortEnd=asc OR ?sortEnd=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -6407,7 +6824,9 @@ "description": "" } }, - "tags": ["Orgs / Users / OOO"] + "tags": [ + "Orgs / Users / OOO" + ] } }, "/v2/organizations/{orgId}/users/{userId}/schedules": { @@ -6473,7 +6892,9 @@ } } }, - "tags": ["Orgs / Users / Schedules"] + "tags": [ + "Orgs / Users / Schedules" + ] }, "get": { "operationId": "OrganizationsSchedulesController_getUserSchedules", @@ -6527,7 +6948,9 @@ } } }, - "tags": ["Orgs / Users / Schedules"] + "tags": [ + "Orgs / Users / Schedules" + ] } }, "/v2/organizations/{orgId}/users/{userId}/schedules/{scheduleId}": { @@ -6591,7 +7014,9 @@ } } }, - "tags": ["Orgs / Users / Schedules"] + "tags": [ + "Orgs / Users / Schedules" + ] }, "patch": { "operationId": "OrganizationsSchedulesController_updateUserSchedule", @@ -6663,7 +7088,9 @@ } } }, - "tags": ["Orgs / Users / Schedules"] + "tags": [ + "Orgs / Users / Schedules" + ] }, "delete": { "operationId": "OrganizationsSchedulesController_deleteUserSchedule", @@ -6725,7 +7152,9 @@ } } }, - "tags": ["Orgs / Users / Schedules"] + "tags": [ + "Orgs / Users / Schedules" + ] } }, "/v2/organizations/{orgId}/webhooks": { @@ -6806,7 +7235,9 @@ } } }, - "tags": ["Orgs / Webhooks"] + "tags": [ + "Orgs / Webhooks" + ] }, "post": { "operationId": "OrganizationsWebhooksController_createOrganizationWebhook", @@ -6870,7 +7301,9 @@ } } }, - "tags": ["Orgs / Webhooks"] + "tags": [ + "Orgs / Webhooks" + ] } }, "/v2/organizations/{orgId}/webhooks/{webhookId}": { @@ -6926,7 +7359,9 @@ } } }, - "tags": ["Orgs / Webhooks"] + "tags": [ + "Orgs / Webhooks" + ] }, "delete": { "operationId": "OrganizationsWebhooksController_deleteWebhook", @@ -6980,7 +7415,9 @@ } } }, - "tags": ["Orgs / Webhooks"] + "tags": [ + "Orgs / Webhooks" + ] }, "patch": { "operationId": "OrganizationsWebhooksController_updateOrgWebhook", @@ -7044,7 +7481,9 @@ } } }, - "tags": ["Orgs / Webhooks"] + "tags": [ + "Orgs / Webhooks" + ] } }, "/v2/api-keys/refresh": { @@ -7085,7 +7524,9 @@ } } }, - "tags": ["Api Keys"] + "tags": [ + "Api Keys" + ] } }, "/v2/bookings": { @@ -7138,7 +7579,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] }, "get": { "operationId": "BookingsController_2024_08_13_getBookings", @@ -7164,7 +7607,13 @@ "type": "array", "items": { "type": "string", - "enum": ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] + "enum": [ + "upcoming", + "recurring", + "past", + "cancelled", + "unconfirmed" + ] } } }, @@ -7305,7 +7754,10 @@ "description": "Sort results by their start time in ascending or descending order.", "example": "?sortStart=asc OR ?sortStart=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -7316,7 +7768,10 @@ "description": "Sort results by their end time in ascending or descending order.", "example": "?sortEnd=asc OR ?sortEnd=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -7327,7 +7782,10 @@ "description": "Sort results by their creation time (when booking was made) in ascending or descending order.", "example": "?sortCreated=asc OR ?sortCreated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -7338,7 +7796,10 @@ "description": "Sort results by their updated time (for example when booking status changes) in ascending or descending order.", "example": "?sortUpdated=asc OR ?sortUpdated=desc", "schema": { - "enum": ["asc", "desc"], + "enum": [ + "asc", + "desc" + ], "type": "string" } }, @@ -7386,7 +7847,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}": { @@ -7426,7 +7889,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/recordings": { @@ -7466,7 +7931,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/transcripts": { @@ -7506,7 +7973,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/reschedule": { @@ -7564,7 +8033,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/cancel": { @@ -7622,7 +8093,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/mark-absent": { @@ -7681,7 +8154,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/reassign": { @@ -7730,7 +8205,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/reassign/{userId}": { @@ -7797,7 +8274,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/confirm": { @@ -7846,7 +8325,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/decline": { @@ -7905,7 +8386,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/calendar-links": { @@ -7954,7 +8437,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/bookings/{bookingUid}/references": { @@ -8020,7 +8505,9 @@ } } }, - "tags": ["Bookings"] + "tags": [ + "Bookings" + ] } }, "/v2/calendars/{calendar}/event/{eventUid}": { @@ -8034,7 +8521,9 @@ "required": true, "in": "path", "schema": { - "enum": ["google"], + "enum": [ + "google" + ], "type": "string" } }, @@ -8069,7 +8558,9 @@ } } }, - "tags": ["Cal Unified Calendars"] + "tags": [ + "Cal Unified Calendars" + ] } }, "/v2/calendars/ics-feed/save": { @@ -8109,7 +8600,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/ics-feed/check": { @@ -8139,7 +8632,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/busy-times": { @@ -8216,7 +8711,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars": { @@ -8246,7 +8743,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/{calendar}/connect": { @@ -8268,7 +8767,10 @@ "required": true, "in": "path", "schema": { - "enum": ["office365", "google"], + "enum": [ + "office365", + "google" + ], "type": "string" } }, @@ -8302,7 +8804,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/{calendar}/save": { @@ -8331,7 +8835,10 @@ "required": true, "in": "path", "schema": { - "enum": ["office365", "google"], + "enum": [ + "office365", + "google" + ], "type": "string" } } @@ -8341,7 +8848,9 @@ "description": "" } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/{calendar}/credentials": { @@ -8354,7 +8863,9 @@ "required": true, "in": "path", "schema": { - "enum": ["apple"], + "enum": [ + "apple" + ], "type": "string" } }, @@ -8383,7 +8894,9 @@ "description": "" } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/{calendar}/check": { @@ -8396,7 +8909,11 @@ "required": true, "in": "path", "schema": { - "enum": ["apple", "google", "office365"], + "enum": [ + "apple", + "google", + "office365" + ], "type": "string" } }, @@ -8422,7 +8939,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/calendars/{calendar}/disconnect": { @@ -8435,7 +8954,11 @@ "required": true, "in": "path", "schema": { - "enum": ["apple", "google", "office365"], + "enum": [ + "apple", + "google", + "office365" + ], "type": "string" } }, @@ -8471,7 +8994,9 @@ } } }, - "tags": ["Calendars"] + "tags": [ + "Calendars" + ] } }, "/v2/conferencing/{app}/connect": { @@ -8485,7 +9010,9 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet"], + "enum": [ + "google-meet" + ], "type": "string" } }, @@ -8511,7 +9038,9 @@ } } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/conferencing/{app}/oauth/auth-url": { @@ -8534,7 +9063,10 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["zoom", "msteams"], + "enum": [ + "zoom", + "msteams" + ], "type": "string" } }, @@ -8567,7 +9099,9 @@ } } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/conferencing/{app}/oauth/callback": { @@ -8589,7 +9123,10 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["zoom", "msteams"], + "enum": [ + "zoom", + "msteams" + ], "type": "string" } }, @@ -8607,7 +9144,9 @@ "description": "" } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/conferencing": { @@ -8637,7 +9176,9 @@ } } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/conferencing/{app}/default": { @@ -8651,7 +9192,12 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet", "zoom", "msteams", "daily-video"], + "enum": [ + "google-meet", + "zoom", + "msteams", + "daily-video" + ], "type": "string" } }, @@ -8677,7 +9223,9 @@ } } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/conferencing/default": { @@ -8707,7 +9255,9 @@ } } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/conferencing/{app}/disconnect": { @@ -8721,7 +9271,11 @@ "in": "path", "description": "Conferencing application type", "schema": { - "enum": ["google-meet", "zoom", "msteams"], + "enum": [ + "google-meet", + "zoom", + "msteams" + ], "type": "string" } }, @@ -8747,7 +9301,9 @@ } } }, - "tags": ["Conferencing"] + "tags": [ + "Conferencing" + ] } }, "/v2/destination-calendars": { @@ -8787,7 +9343,9 @@ } } }, - "tags": ["Destination Calendars"] + "tags": [ + "Destination Calendars" + ] } }, "/v2/event-types": { @@ -8837,7 +9395,9 @@ } } }, - "tags": ["Event Types"] + "tags": [ + "Event Types" + ] }, "get": { "operationId": "EventTypesController_2024_06_14_getEventTypes", @@ -8911,7 +9471,9 @@ } } }, - "tags": ["Event Types"] + "tags": [ + "Event Types" + ] } }, "/v2/event-types/{eventTypeId}": { @@ -8959,7 +9521,9 @@ } } }, - "tags": ["Event Types"] + "tags": [ + "Event Types" + ] }, "patch": { "operationId": "EventTypesController_2024_06_14_updateEventType", @@ -9015,7 +9579,9 @@ } } }, - "tags": ["Event Types"] + "tags": [ + "Event Types" + ] }, "delete": { "operationId": "EventTypesController_2024_06_14_deleteEventType", @@ -9061,7 +9627,9 @@ } } }, - "tags": ["Event Types"] + "tags": [ + "Event Types" + ] } }, "/v2/event-types/{eventTypeId}/webhooks": { @@ -9109,7 +9677,9 @@ } } }, - "tags": ["Event Types / Webhooks"] + "tags": [ + "Event Types / Webhooks" + ] }, "get": { "operationId": "EventTypeWebhooksController_getEventTypeWebhooks", @@ -9170,7 +9740,9 @@ } } }, - "tags": ["Event Types / Webhooks"] + "tags": [ + "Event Types / Webhooks" + ] }, "delete": { "operationId": "EventTypeWebhooksController_deleteAllEventTypeWebhooks", @@ -9206,7 +9778,9 @@ } } }, - "tags": ["Event Types / Webhooks"] + "tags": [ + "Event Types / Webhooks" + ] } }, "/v2/event-types/{eventTypeId}/webhooks/{webhookId}": { @@ -9254,7 +9828,9 @@ } } }, - "tags": ["Event Types / Webhooks"] + "tags": [ + "Event Types / Webhooks" + ] }, "get": { "operationId": "EventTypeWebhooksController_getEventTypeWebhook", @@ -9282,7 +9858,9 @@ } } }, - "tags": ["Event Types / Webhooks"] + "tags": [ + "Event Types / Webhooks" + ] }, "delete": { "operationId": "EventTypeWebhooksController_deleteEventTypeWebhook", @@ -9310,7 +9888,9 @@ } } }, - "tags": ["Event Types / Webhooks"] + "tags": [ + "Event Types / Webhooks" + ] } }, "/v2/organizations/{orgId}/organizations": { @@ -9368,7 +9948,9 @@ } } }, - "tags": ["Managed Orgs"] + "tags": [ + "Managed Orgs" + ] }, "get": { "operationId": "OrganizationsOrganizationsController_getOrganizations", @@ -9469,7 +10051,9 @@ } } }, - "tags": ["Managed Orgs"] + "tags": [ + "Managed Orgs" + ] } }, "/v2/organizations/{orgId}/organizations/{managedOrganizationId}": { @@ -9517,7 +10101,9 @@ } } }, - "tags": ["Managed Orgs"] + "tags": [ + "Managed Orgs" + ] }, "patch": { "operationId": "OrganizationsOrganizationsController_updateOrganization", @@ -9581,7 +10167,9 @@ } } }, - "tags": ["Managed Orgs"] + "tags": [ + "Managed Orgs" + ] }, "delete": { "operationId": "OrganizationsOrganizationsController_deleteOrganization", @@ -9627,7 +10215,9 @@ } } }, - "tags": ["Managed Orgs"] + "tags": [ + "Managed Orgs" + ] } }, "/v2/me": { @@ -9657,7 +10247,9 @@ } } }, - "tags": ["Me"] + "tags": [ + "Me" + ] }, "patch": { "operationId": "MeController_updateMe", @@ -9695,7 +10287,9 @@ } } }, - "tags": ["Me"] + "tags": [ + "Me" + ] } }, "/v2/oauth-clients": { @@ -9735,7 +10329,9 @@ } } }, - "tags": ["OAuth Clients"] + "tags": [ + "OAuth Clients" + ] }, "get": { "operationId": "OAuthClientsController_getOAuthClients", @@ -9763,7 +10359,9 @@ } } }, - "tags": ["OAuth Clients"] + "tags": [ + "OAuth Clients" + ] } }, "/v2/oauth-clients/{clientId}": { @@ -9801,7 +10399,9 @@ } } }, - "tags": ["OAuth Clients"] + "tags": [ + "OAuth Clients" + ] }, "patch": { "operationId": "OAuthClientsController_updateOAuthClient", @@ -9847,7 +10447,9 @@ } } }, - "tags": ["OAuth Clients"] + "tags": [ + "OAuth Clients" + ] }, "delete": { "operationId": "OAuthClientsController_deleteOAuthClient", @@ -9883,7 +10485,9 @@ } } }, - "tags": ["OAuth Clients"] + "tags": [ + "OAuth Clients" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails/verification-code/request": { @@ -9924,7 +10528,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones/verification-code/request": { @@ -9965,7 +10571,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails/verification-code/verify": { @@ -10014,7 +10622,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones/verification-code/verify": { @@ -10063,7 +10673,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails": { @@ -10126,7 +10738,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones": { @@ -10189,7 +10803,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails/{id}": { @@ -10235,7 +10851,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones/{id}": { @@ -10281,7 +10899,9 @@ } } }, - "tags": ["Organization Team Verified Resources"] + "tags": [ + "Organization Team Verified Resources" + ] } }, "/v2/routing-forms/{routingFormId}/calculate-slots": { @@ -10346,7 +10966,10 @@ "description": "Format of slot times in response. Use 'range' to get start and end times.", "example": "range", "schema": { - "enum": ["range", "time"], + "enum": [ + "range", + "time" + ], "type": "string" } }, @@ -10381,7 +11004,9 @@ } } }, - "tags": ["Routing forms"] + "tags": [ + "Routing forms" + ] } }, "/v2/schedules": { @@ -10432,7 +11057,9 @@ } } }, - "tags": ["Schedules"] + "tags": [ + "Schedules" + ] }, "get": { "operationId": "SchedulesController_2024_06_11_getSchedules", @@ -10471,7 +11098,9 @@ } } }, - "tags": ["Schedules"] + "tags": [ + "Schedules" + ] } }, "/v2/schedules/default": { @@ -10512,7 +11141,9 @@ } } }, - "tags": ["Schedules"] + "tags": [ + "Schedules" + ] } }, "/v2/schedules/{scheduleId}": { @@ -10560,7 +11191,9 @@ } } }, - "tags": ["Schedules"] + "tags": [ + "Schedules" + ] }, "patch": { "operationId": "SchedulesController_2024_06_11_updateSchedule", @@ -10616,7 +11249,9 @@ } } }, - "tags": ["Schedules"] + "tags": [ + "Schedules" + ] }, "delete": { "operationId": "SchedulesController_2024_06_11_deleteSchedule", @@ -10662,7 +11297,9 @@ } } }, - "tags": ["Schedules"] + "tags": [ + "Schedules" + ] } }, "/v2/selected-calendars": { @@ -10702,7 +11339,9 @@ } } }, - "tags": ["Selected Calendars"] + "tags": [ + "Selected Calendars" + ] }, "delete": { "operationId": "SelectedCalendarsController_deleteSelectedCalendar", @@ -10762,7 +11401,9 @@ } } }, - "tags": ["Selected Calendars"] + "tags": [ + "Selected Calendars" + ] } }, "/v2/slots": { @@ -10965,7 +11606,9 @@ } } }, - "tags": ["Slots"] + "tags": [ + "Slots" + ] } }, "/v2/slots/reservations": { @@ -11025,7 +11668,9 @@ } } }, - "tags": ["Slots"] + "tags": [ + "Slots" + ] } }, "/v2/slots/reservations/{uid}": { @@ -11064,7 +11709,9 @@ } } }, - "tags": ["Slots"] + "tags": [ + "Slots" + ] }, "patch": { "operationId": "SlotsController_2024_09_04_updateReservedSlot", @@ -11111,7 +11758,9 @@ } } }, - "tags": ["Slots"] + "tags": [ + "Slots" + ] }, "delete": { "operationId": "SlotsController_2024_09_04_deleteReservedSlot", @@ -11151,7 +11800,9 @@ } } }, - "tags": ["Slots"] + "tags": [ + "Slots" + ] } }, "/v2/stripe/connect": { @@ -11181,7 +11832,9 @@ } } }, - "tags": ["Stripe"] + "tags": [ + "Stripe" + ] } }, "/v2/stripe/save": { @@ -11218,7 +11871,9 @@ } } }, - "tags": ["Stripe"] + "tags": [ + "Stripe" + ] } }, "/v2/stripe/check": { @@ -11248,7 +11903,9 @@ } } }, - "tags": ["Stripe"] + "tags": [ + "Stripe" + ] } }, "/v2/teams": { @@ -11288,7 +11945,9 @@ } } }, - "tags": ["Teams"] + "tags": [ + "Teams" + ] }, "get": { "operationId": "TeamsController_getTeams", @@ -11316,7 +11975,9 @@ } } }, - "tags": ["Teams"] + "tags": [ + "Teams" + ] } }, "/v2/teams/{teamId}": { @@ -11354,7 +12015,9 @@ } } }, - "tags": ["Teams"] + "tags": [ + "Teams" + ] }, "patch": { "operationId": "TeamsController_updateTeam", @@ -11400,7 +12063,9 @@ } } }, - "tags": ["Teams"] + "tags": [ + "Teams" + ] }, "delete": { "operationId": "TeamsController_deleteTeam", @@ -11436,7 +12101,9 @@ } } }, - "tags": ["Teams"] + "tags": [ + "Teams" + ] } }, "/v2/teams/{teamId}/event-types": { @@ -11484,7 +12151,9 @@ } } }, - "tags": ["Teams / Event Types"] + "tags": [ + "Teams / Event Types" + ] }, "get": { "operationId": "TeamsEventTypesController_getTeamEventTypes", @@ -11529,7 +12198,9 @@ } } }, - "tags": ["Teams / Event Types"] + "tags": [ + "Teams / Event Types" + ] } }, "/v2/teams/{teamId}/event-types/{eventTypeId}": { @@ -11575,7 +12246,9 @@ } } }, - "tags": ["Teams / Event Types"] + "tags": [ + "Teams / Event Types" + ] }, "patch": { "operationId": "TeamsEventTypesController_updateTeamEventType", @@ -11629,7 +12302,9 @@ } } }, - "tags": ["Teams / Event Types"] + "tags": [ + "Teams / Event Types" + ] }, "delete": { "operationId": "TeamsEventTypesController_deleteTeamEventType", @@ -11673,7 +12348,9 @@ } } }, - "tags": ["Teams / Event Types"] + "tags": [ + "Teams / Event Types" + ] } }, "/v2/teams/{teamId}/event-types/{eventTypeId}/create-phone-call": { @@ -11729,7 +12406,9 @@ } } }, - "tags": ["Teams / Event Types"] + "tags": [ + "Teams / Event Types" + ] } }, "/v2/teams/{teamId}/memberships": { @@ -11777,7 +12456,9 @@ } } }, - "tags": ["Teams / Memberships"] + "tags": [ + "Teams / Memberships" + ] }, "get": { "operationId": "TeamsMembershipsController_getTeamMemberships", @@ -11838,7 +12519,9 @@ } } }, - "tags": ["Teams / Memberships"] + "tags": [ + "Teams / Memberships" + ] } }, "/v2/teams/{teamId}/memberships/{membershipId}": { @@ -11884,7 +12567,9 @@ } } }, - "tags": ["Teams / Memberships"] + "tags": [ + "Teams / Memberships" + ] }, "patch": { "operationId": "TeamsMembershipsController_updateTeamMembership", @@ -11938,7 +12623,9 @@ } } }, - "tags": ["Teams / Memberships"] + "tags": [ + "Teams / Memberships" + ] }, "delete": { "operationId": "TeamsMembershipsController_deleteTeamMembership", @@ -11982,7 +12669,9 @@ } } }, - "tags": ["Teams / Memberships"] + "tags": [ + "Teams / Memberships" + ] } }, "/v2/teams/{teamId}/verified-resources/emails/verification-code/request": { @@ -12023,7 +12712,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/phones/verification-code/request": { @@ -12064,7 +12755,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/emails/verification-code/verify": { @@ -12113,7 +12806,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/phones/verification-code/verify": { @@ -12162,7 +12857,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/emails": { @@ -12225,7 +12922,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/phones": { @@ -12288,7 +12987,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/emails/{id}": { @@ -12334,7 +13035,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/teams/{teamId}/verified-resources/phones/{id}": { @@ -12380,7 +13083,9 @@ } } }, - "tags": ["Teams Verified Resources"] + "tags": [ + "Teams Verified Resources" + ] } }, "/v2/verified-resources/emails/verification-code/request": { @@ -12421,7 +13126,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/phones/verification-code/request": { @@ -12462,7 +13169,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/emails/verification-code/verify": { @@ -12503,7 +13212,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/phones/verification-code/verify": { @@ -12544,7 +13255,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/emails": { @@ -12599,7 +13312,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/phones": { @@ -12654,7 +13369,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/emails/{id}": { @@ -12692,7 +13409,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/verified-resources/phones/{id}": { @@ -12730,7 +13449,9 @@ } } }, - "tags": ["Verified Resources"] + "tags": [ + "Verified Resources" + ] } }, "/v2/webhooks": { @@ -12770,7 +13491,9 @@ } } }, - "tags": ["Webhooks"] + "tags": [ + "Webhooks" + ] }, "get": { "operationId": "WebhooksController_getWebhooks", @@ -12824,7 +13547,9 @@ } } }, - "tags": ["Webhooks"] + "tags": [ + "Webhooks" + ] } }, "/v2/webhooks/{webhookId}": { @@ -12872,7 +13597,9 @@ } } }, - "tags": ["Webhooks"] + "tags": [ + "Webhooks" + ] }, "get": { "operationId": "WebhooksController_getWebhook", @@ -12900,7 +13627,9 @@ } } }, - "tags": ["Webhooks"] + "tags": [ + "Webhooks" + ] }, "delete": { "operationId": "WebhooksController_deleteWebhook", @@ -12936,7 +13665,9 @@ } } }, - "tags": ["Webhooks"] + "tags": [ + "Webhooks" + ] } } }, @@ -13079,7 +13810,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -13088,7 +13822,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateManagedUserInput": { "type": "object", @@ -13104,14 +13841,25 @@ }, "timeFormat": { "type": "number", - "enum": [12, 24], + "enum": [ + 12, + 24 + ], "example": 12, "description": "Must be a number 12 or 24" }, "weekStart": { "type": "string", "example": "Monday", - "enum": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + "enum": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ] }, "timeZone": { "type": "string", @@ -13185,7 +13933,10 @@ } } }, - "required": ["email", "name"] + "required": [ + "email", + "name" + ] }, "CreateManagedUserData": { "type": "object", @@ -13208,7 +13959,13 @@ "type": "number" } }, - "required": ["accessToken", "refreshToken", "user", "accessTokenExpiresAt", "refreshTokenExpiresAt"] + "required": [ + "accessToken", + "refreshToken", + "user", + "accessTokenExpiresAt", + "refreshTokenExpiresAt" + ] }, "CreateManagedUserOutput": { "type": "object", @@ -13216,7 +13973,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/CreateManagedUserData" @@ -13225,7 +13985,10 @@ "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetManagedUserOutput": { "type": "object", @@ -13233,13 +13996,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ManagedUserOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateManagedUserInput": { "type": "object", @@ -13252,7 +14021,10 @@ }, "timeFormat": { "type": "number", - "enum": [12, 24], + "enum": [ + 12, + 24 + ], "example": 12, "description": "Must be 12 or 24" }, @@ -13261,7 +14033,15 @@ }, "weekStart": { "type": "string", - "enum": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], + "enum": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ], "example": "Monday" }, "timeZone": { @@ -13353,7 +14133,12 @@ "type": "number" } }, - "required": ["accessToken", "refreshToken", "accessTokenExpiresAt", "refreshTokenExpiresAt"] + "required": [ + "accessToken", + "refreshToken", + "accessTokenExpiresAt", + "refreshTokenExpiresAt" + ] }, "KeysResponseDto": { "type": "object", @@ -13361,13 +14146,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/KeysDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateOAuthClientInput": { "type": "object", @@ -13427,7 +14218,11 @@ "description": "If true and if managed user has calendar connected, calendar events will be created. Disable it if you manually create calendar events. Default to true." } }, - "required": ["name", "redirectUris", "permissions"] + "required": [ + "name", + "redirectUris", + "permissions" + ] }, "CreateOAuthClientOutput": { "type": "object", @@ -13441,14 +14236,20 @@ "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoib2F1dGgtY2xpZW50Iiwi" } }, - "required": ["clientId", "clientSecret"] + "required": [ + "clientId", + "clientSecret" + ] }, "CreateOAuthClientResponseDto": { "type": "object", "properties": { "status": { "type": "string", - "enum": ["success", "error"], + "enum": [ + "success", + "error" + ], "example": "success" }, "data": { @@ -13463,7 +14264,10 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "PlatformOAuthClientDto": { "type": "object", @@ -13498,14 +14302,19 @@ "PROFILE_WRITE" ] }, - "example": ["BOOKING_READ", "BOOKING_WRITE"] + "example": [ + "BOOKING_READ", + "BOOKING_WRITE" + ] }, "logo": { "type": "object", "example": "https://example.com/logo.png" }, "redirectUris": { - "example": ["https://example.com/callback"], + "example": [ + "https://example.com/callback" + ], "type": "array", "items": { "type": "string" @@ -13566,7 +14375,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -13575,7 +14387,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetOAuthClientResponseDto": { "type": "object", @@ -13583,13 +14398,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/PlatformOAuthClientDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOAuthClientInput": { "type": "object", @@ -13636,7 +14457,9 @@ "description": "Managed user's refresh token." } }, - "required": ["refreshToken"] + "required": [ + "refreshToken" + ] }, "RefreshApiKeyInput": { "type": "object", @@ -13662,7 +14485,9 @@ "type": "string" } }, - "required": ["apiKey"] + "required": [ + "apiKey" + ] }, "RefreshApiKeyOutput": { "type": "object", @@ -13670,32 +14495,49 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ApiKeyOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "BookerLayouts_2024_06_14": { "type": "object", "properties": { "defaultLayout": { "type": "string", - "enum": ["month", "week", "column"] + "enum": [ + "month", + "week", + "column" + ] }, "enabledLayouts": { "type": "array", "description": "Array of valid layouts - month, week or column", "items": { "type": "string", - "enum": ["month", "week", "column"] + "enum": [ + "month", + "week", + "column" + ] } } }, - "required": ["defaultLayout", "enabledLayouts"] - }, + "required": [ + "defaultLayout", + "enabledLayouts" + ] + }, "EventTypeColor_2024_06_14": { "type": "object", "properties": { @@ -13710,7 +14552,10 @@ "example": "#fafafa" } }, - "required": ["lightThemeHex", "darkThemeHex"] + "required": [ + "lightThemeHex", + "darkThemeHex" + ] }, "DestinationCalendar_2024_06_14": { "type": "object", @@ -13724,7 +14569,10 @@ "description": "The external ID of the destination calendar. Refer to the /api/v2/calendars endpoint to retrieve the external IDs of your connected calendars." } }, - "required": ["integration", "externalId"] + "required": [ + "integration", + "externalId" + ] }, "InputAddressLocation_2024_06_14": { "type": "object", @@ -13742,7 +14590,11 @@ "type": "boolean" } }, - "required": ["type", "address", "public"] + "required": [ + "type", + "address", + "public" + ] }, "InputLinkLocation_2024_06_14": { "type": "object", @@ -13760,7 +14612,11 @@ "type": "boolean" } }, - "required": ["type", "link", "public"] + "required": [ + "type", + "link", + "public" + ] }, "InputIntegrationLocation_2024_06_14": { "type": "object", @@ -13773,10 +14629,18 @@ "integration": { "type": "string", "example": "cal-video", - "enum": ["cal-video", "google-meet", "office365-video", "zoom"] + "enum": [ + "cal-video", + "google-meet", + "office365-video", + "zoom" + ] } }, - "required": ["type", "integration"] + "required": [ + "type", + "integration" + ] }, "InputPhoneLocation_2024_06_14": { "type": "object", @@ -13794,7 +14658,11 @@ "type": "boolean" } }, - "required": ["type", "phone", "public"] + "required": [ + "type", + "phone", + "public" + ] }, "PhoneFieldInput_2024_06_14": { "type": "object", @@ -13827,7 +14695,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "AddressFieldInput_2024_06_14": { "type": "object", @@ -13862,7 +14737,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "TextFieldInput_2024_06_14": { "type": "object", @@ -13897,7 +14779,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "NumberFieldInput_2024_06_14": { "type": "object", @@ -13932,7 +14821,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "TextAreaFieldInput_2024_06_14": { "type": "object", @@ -13967,7 +14863,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "SelectFieldInput_2024_06_14": { "type": "object", @@ -13994,7 +14897,10 @@ "example": "Select..." }, "options": { - "example": ["Option 1", "Option 2"], + "example": [ + "Option 1", + "Option 2" + ], "type": "array", "items": { "type": "string" @@ -14009,7 +14915,15 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "options", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "options", + "hidden" + ] }, "MultiSelectFieldInput_2024_06_14": { "type": "object", @@ -14032,7 +14946,10 @@ "type": "boolean" }, "options": { - "example": ["Option 1", "Option 2"], + "example": [ + "Option 1", + "Option 2" + ], "type": "array", "items": { "type": "string" @@ -14047,7 +14964,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "options", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden" + ] }, "MultiEmailFieldInput_2024_06_14": { "type": "object", @@ -14082,7 +15006,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "CheckboxGroupFieldInput_2024_06_14": { "type": "object", @@ -14105,7 +15036,10 @@ "type": "boolean" }, "options": { - "example": ["Checkbox 1", "Checkbox 2"], + "example": [ + "Checkbox 1", + "Checkbox 2" + ], "type": "array", "items": { "type": "string" @@ -14120,7 +15054,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "options", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden" + ] }, "RadioGroupFieldInput_2024_06_14": { "type": "object", @@ -14143,7 +15084,10 @@ "type": "boolean" }, "options": { - "example": ["Radio 1", "Radio 2"], + "example": [ + "Radio 1", + "Radio 2" + ], "type": "array", "items": { "type": "string" @@ -14158,7 +15102,14 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "options", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden" + ] }, "BooleanFieldInput_2024_06_14": { "type": "object", @@ -14189,7 +15140,13 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden" + ] }, "UrlFieldInput_2024_06_14": { "type": "object", @@ -14224,14 +15181,25 @@ "description": "If true show under event type settings but don't show this booking field in the Booker. If false show in both." } }, - "required": ["type", "slug", "label", "required", "placeholder", "hidden"] + "required": [ + "type", + "slug", + "label", + "required", + "placeholder", + "hidden" + ] }, "BusinessDaysWindow_2024_06_14": { "type": "object", "properties": { "type": { "type": "string", - "enum": ["businessDays", "calendarDays", "range"], + "enum": [ + "businessDays", + "calendarDays", + "range" + ], "description": "Whether the window should be business days, calendar days or a range of dates" }, "value": { @@ -14245,14 +15213,21 @@ "description": "\n Determines the behavior of the booking window:\n - If **true**, the window is rolling. This means the number of available days will always be equal the specified 'value' \n and adjust dynamically as bookings are made. For example, if 'value' is 3 and availability is only on Mondays, \n a booker attempting to schedule on November 10 will see slots on November 11, 18, and 25. As one of these days \n becomes fully booked, a new day (e.g., December 2) will open up to ensure 3 available days are always visible.\n - If **false**, the window is fixed. This means the booking window only considers the next 'value' days from the\n moment someone is trying to book. For example, if 'value' is 3, availability is only on Mondays, and the current \n date is November 10, the booker will only see slots on November 11 because the window is restricted to the next \n 3 calendar days (November 10–12).\n " } }, - "required": ["type", "value"] + "required": [ + "type", + "value" + ] }, "CalendarDaysWindow_2024_06_14": { "type": "object", "properties": { "type": { "type": "string", - "enum": ["businessDays", "calendarDays", "range"], + "enum": [ + "businessDays", + "calendarDays", + "range" + ], "description": "Whether the window should be business days, calendar days or a range of dates" }, "value": { @@ -14266,18 +15241,28 @@ "description": "\n Determines the behavior of the booking window:\n - If **true**, the window is rolling. This means the number of available days will always be equal the specified 'value' \n and adjust dynamically as bookings are made. For example, if 'value' is 3 and availability is only on Mondays, \n a booker attempting to schedule on November 10 will see slots on November 11, 18, and 25. As one of these days \n becomes fully booked, a new day (e.g., December 2) will open up to ensure 3 available days are always visible.\n - If **false**, the window is fixed. This means the booking window only considers the next 'value' days from the\n moment someone is trying to book. For example, if 'value' is 3, availability is only on Mondays, and the current \n date is November 10, the booker will only see slots on November 11 because the window is restricted to the next \n 3 calendar days (November 10–12).\n " } }, - "required": ["type", "value"] + "required": [ + "type", + "value" + ] }, "RangeWindow_2024_06_14": { "type": "object", "properties": { "type": { "type": "string", - "enum": ["businessDays", "calendarDays", "range"], + "enum": [ + "businessDays", + "calendarDays", + "range" + ], "description": "Whether the window should be business days, calendar days or a range of dates" }, "value": { - "example": ["2030-09-05", "2030-09-09"], + "example": [ + "2030-09-05", + "2030-09-09" + ], "description": "Date range for when this event can be booked.", "type": "array", "items": { @@ -14285,7 +15270,10 @@ } } }, - "required": ["type", "value"] + "required": [ + "type", + "value" + ] }, "BaseBookingLimitsCount_2024_06_14": { "type": "object", @@ -14326,7 +15314,9 @@ "default": false } }, - "required": ["disabled"] + "required": [ + "disabled" + ] }, "BaseBookingLimitsDuration_2024_06_14": { "type": "object", @@ -14368,10 +15358,18 @@ }, "frequency": { "type": "string", - "enum": ["yearly", "monthly", "weekly"] + "enum": [ + "yearly", + "monthly", + "weekly" + ] } }, - "required": ["interval", "occurrences", "frequency"] + "required": [ + "interval", + "occurrences", + "frequency" + ] }, "NoticeThreshold_2024_06_14": { "type": "object", @@ -14387,7 +15385,10 @@ "example": 30 } }, - "required": ["unit", "count"] + "required": [ + "unit", + "count" + ] }, "BaseConfirmationPolicy_2024_06_14": { "type": "object", @@ -14395,7 +15396,10 @@ "type": { "type": "string", "description": "The policy that determines when confirmation is required", - "enum": ["always", "time"], + "enum": [ + "always", + "time" + ], "example": "always" }, "noticeThreshold": { @@ -14411,7 +15415,10 @@ "description": "Unconfirmed bookings still block calendar slots." } }, - "required": ["type", "blockUnconfirmedBookingsInBooker"] + "required": [ + "type", + "blockUnconfirmedBookingsInBooker" + ] }, "Seats_2024_06_14": { "type": "object", @@ -14432,7 +15439,11 @@ "example": true } }, - "required": ["seatsPerTimeSlot", "showAttendeeInfo", "showAvailabilityCount"] + "required": [ + "seatsPerTimeSlot", + "showAttendeeInfo", + "showAvailabilityCount" + ] }, "InputAttendeeAddressLocation_2024_06_14": { "type": "object", @@ -14443,7 +15454,9 @@ "description": "only allowed value for type is `attendeeAddress`" } }, - "required": ["type"] + "required": [ + "type" + ] }, "InputAttendeePhoneLocation_2024_06_14": { "type": "object", @@ -14454,7 +15467,9 @@ "description": "only allowed value for type is `attendeePhone`" } }, - "required": ["type"] + "required": [ + "type" + ] }, "InputAttendeeDefinedLocation_2024_06_14": { "type": "object", @@ -14465,7 +15480,9 @@ "description": "only allowed value for type is `attendeeDefined`" } }, - "required": ["type"] + "required": [ + "type" + ] }, "NameDefaultFieldInput_2024_06_14": { "type": "object", @@ -14486,7 +15503,11 @@ "description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&name=bob`, the name field will be prefilled with this value and disabled. In case of Booker atom need to pass 'name' to defaultFormValues prop with the desired value e.g. `defaultFormValues={{name: 'bob'}}`. See guide https://cal.com/docs/platform/guides/booking-fields" } }, - "required": ["type", "label", "placeholder"] + "required": [ + "type", + "label", + "placeholder" + ] }, "EmailDefaultFieldInput_2024_06_14": { "type": "object", @@ -14515,7 +15536,11 @@ "description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&email=bob@gmail.com`, the email field will be prefilled with this value and disabled. In case of Booker atom need to pass 'email' to defaultFormValues prop with the desired value e.g. `defaultFormValues={{email: 'bob@gmail.com'}}`. See guide https://cal.com/docs/platform/guides/booking-field" } }, - "required": ["type", "label", "placeholder"] + "required": [ + "type", + "label", + "placeholder" + ] }, "TitleDefaultFieldInput_2024_06_14": { "type": "object", @@ -14543,7 +15568,9 @@ "description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&title=journey`, the title field will be prefilled with this value and disabled. In case of Booker atom need to pass 'title' to defaultFormValues prop with the desired value e.g. `defaultFormValues={{title: 'very important meeting'}}`. See guide https://cal.com/docs/platform/guides/booking-field" } }, - "required": ["slug"] + "required": [ + "slug" + ] }, "LocationDefaultFieldInput_2024_06_14": { "type": "object", @@ -14557,7 +15584,9 @@ "type": "string" } }, - "required": ["slug"] + "required": [ + "slug" + ] }, "NotesDefaultFieldInput_2024_06_14": { "type": "object", @@ -14585,7 +15614,9 @@ "description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `¬es=journey`, the notes field will be prefilled with this value and disabled. In case of Booker atom need to pass 'notes' to defaultFormValues prop with the desired value e.g. `defaultFormValues={{notes: 'bring notebook and paper'}}`. See guide https://cal.com/docs/platform/guides/booking-field" } }, - "required": ["slug"] + "required": [ + "slug" + ] }, "GuestsDefaultFieldInput_2024_06_14": { "type": "object", @@ -14613,7 +15644,9 @@ "description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&guests=bob@cal.com`, the guests field will be prefilled with this value and disabled. In case of Booker atom need to pass 'guests' to defaultFormValues prop with the desired value e.g. `defaultFormValues={{guests: ['bob@gmail.com', 'alice@gmail.com']}}`. See guide https://cal.com/docs/platform/guides/booking-field" } }, - "required": ["slug"] + "required": [ + "slug" + ] }, "RescheduleReasonDefaultFieldInput_2024_06_14": { "type": "object", @@ -14641,7 +15674,9 @@ "description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&rescheduleReason=travel`, the rescheduleReason field will be prefilled with this value and disabled. In case of Booker atom need to pass 'rescheduleReason' to defaultFormValues prop with the desired value e.g. `defaultFormValues={{rescheduleReason: 'bob'}}`. See guide https://cal.com/docs/platform/guides/booking-field" } }, - "required": ["slug"] + "required": [ + "slug" + ] }, "InputOrganizersDefaultApp_2024_06_14": { "type": "object", @@ -14652,7 +15687,9 @@ "description": "only allowed value for type is `organizersDefaultApp`" } }, - "required": ["type"] + "required": [ + "type" + ] }, "CalVideoSettings": { "type": "object", @@ -14679,7 +15716,11 @@ "example": 60 }, "lengthInMinutesOptions": { - "example": [15, 30, 60], + "example": [ + 15, + 30, + 60 + ], "description": "If you want that user can choose between different lengths of the event you can specify them here. Must include the provided `lengthInMinutes`.", "type": "array", "items": { @@ -14948,7 +15989,11 @@ } } }, - "required": ["lengthInMinutes", "title", "slug"] + "required": [ + "lengthInMinutes", + "title", + "slug" + ] }, "OutputAddressLocation_2024_06_14": { "type": "object", @@ -14977,7 +16022,11 @@ "type": "boolean" } }, - "required": ["type", "address", "public"] + "required": [ + "type", + "address", + "public" + ] }, "OutputLinkLocation_2024_06_14": { "type": "object", @@ -15005,7 +16054,11 @@ "type": "boolean" } }, - "required": ["type", "link", "public"] + "required": [ + "type", + "link", + "public" + ] }, "OutputIntegrationLocation_2024_06_14": { "type": "object", @@ -15071,7 +16124,10 @@ "description": "Credential ID associated with the integration" } }, - "required": ["type", "integration"] + "required": [ + "type", + "integration" + ] }, "OutputPhoneLocation_2024_06_14": { "type": "object", @@ -15099,7 +16155,11 @@ "type": "boolean" } }, - "required": ["type", "phone", "public"] + "required": [ + "type", + "phone", + "public" + ] }, "OutputOrganizersDefaultAppLocation_2024_06_14": { "type": "object", @@ -15122,7 +16182,9 @@ "description": "only allowed value for type is `organizersDefaultApp`" } }, - "required": ["type"] + "required": [ + "type" + ] }, "OutputUnknownLocation_2024_06_14": { "type": "object", @@ -15148,7 +16210,10 @@ "type": "string" } }, - "required": ["type", "location"] + "required": [ + "type", + "location" + ] }, "EmailDefaultFieldOutput_2024_06_14": { "type": "object", @@ -15205,7 +16270,11 @@ "default": "email" } }, - "required": ["type", "isDefault", "slug"] + "required": [ + "type", + "isDefault", + "slug" + ] }, "NameDefaultFieldOutput_2024_06_14": { "type": "object", @@ -15256,7 +16325,12 @@ "type": "boolean" } }, - "required": ["type", "isDefault", "slug", "required"] + "required": [ + "type", + "isDefault", + "slug", + "required" + ] }, "LocationDefaultFieldOutput_2024_06_14": { "type": "object", @@ -15287,14 +16361,26 @@ "type": "string" } }, - "required": ["isDefault", "slug", "type", "required", "hidden"] + "required": [ + "isDefault", + "slug", + "type", + "required", + "hidden" + ] }, "RescheduleReasonDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { "slug": { "type": "string", - "enum": ["title", "location", "notes", "guests", "rescheduleReason"], + "enum": [ + "title", + "location", + "notes", + "guests", + "rescheduleReason" + ], "example": "rescheduleReason", "description": "only allowed value for type is `rescheduleReason`", "default": "rescheduleReason" @@ -15327,14 +16413,24 @@ "default": "textarea" } }, - "required": ["slug", "isDefault", "type"] + "required": [ + "slug", + "isDefault", + "type" + ] }, "TitleDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { "slug": { "type": "string", - "enum": ["title", "location", "notes", "guests", "rescheduleReason"], + "enum": [ + "title", + "location", + "notes", + "guests", + "rescheduleReason" + ], "example": "title", "description": "only allowed value for type is `title`", "default": "title" @@ -15367,14 +16463,24 @@ "default": "text" } }, - "required": ["slug", "isDefault", "type"] + "required": [ + "slug", + "isDefault", + "type" + ] }, "NotesDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { "slug": { "type": "string", - "enum": ["title", "location", "notes", "guests", "rescheduleReason"], + "enum": [ + "title", + "location", + "notes", + "guests", + "rescheduleReason" + ], "example": "notes", "description": "only allowed value for type is `notes`", "default": "notes" @@ -15407,14 +16513,24 @@ "default": "textarea" } }, - "required": ["slug", "isDefault", "type"] + "required": [ + "slug", + "isDefault", + "type" + ] }, "GuestsDefaultFieldOutput_2024_06_14": { "type": "object", "properties": { "slug": { "type": "string", - "enum": ["title", "location", "notes", "guests", "rescheduleReason"], + "enum": [ + "title", + "location", + "notes", + "guests", + "rescheduleReason" + ], "example": "guests", "description": "only allowed value for type is `guests`", "default": "guests" @@ -15447,7 +16563,11 @@ "default": "multiemail" } }, - "required": ["slug", "isDefault", "type"] + "required": [ + "slug", + "isDefault", + "type" + ] }, "AddressFieldOutput_2024_06_14": { "type": "object", @@ -15504,7 +16624,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "BooleanFieldOutput_2024_06_14": { "type": "object", @@ -15557,7 +16684,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "CheckboxGroupFieldOutput_2024_06_14": { "type": "object", @@ -15596,7 +16730,10 @@ "type": "boolean" }, "options": { - "example": ["Checkbox 1", "Checkbox 2"], + "example": [ + "Checkbox 1", + "Checkbox 2" + ], "type": "array", "items": { "type": "string" @@ -15617,7 +16754,15 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "options", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden", + "isDefault" + ] }, "MultiEmailFieldOutput_2024_06_14": { "type": "object", @@ -15674,7 +16819,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "MultiSelectFieldOutput_2024_06_14": { "type": "object", @@ -15713,7 +16865,10 @@ "type": "boolean" }, "options": { - "example": ["Option 1", "Option 2"], + "example": [ + "Option 1", + "Option 2" + ], "type": "array", "items": { "type": "string" @@ -15734,7 +16889,15 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "options", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden", + "isDefault" + ] }, "UrlFieldOutput_2024_06_14": { "type": "object", @@ -15791,7 +16954,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "NumberFieldOutput_2024_06_14": { "type": "object", @@ -15848,7 +17018,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "PhoneFieldOutput_2024_06_14": { "type": "object", @@ -15903,7 +17080,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "RadioGroupFieldOutput_2024_06_14": { "type": "object", @@ -15942,7 +17126,10 @@ "type": "boolean" }, "options": { - "example": ["Radio 1", "Radio 2"], + "example": [ + "Radio 1", + "Radio 2" + ], "type": "array", "items": { "type": "string" @@ -15963,7 +17150,15 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "options", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden", + "isDefault" + ] }, "SelectFieldOutput_2024_06_14": { "type": "object", @@ -16006,7 +17201,10 @@ "example": "Select..." }, "options": { - "example": ["Option 1", "Option 2"], + "example": [ + "Option 1", + "Option 2" + ], "type": "array", "items": { "type": "string" @@ -16027,7 +17225,15 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "options", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "options", + "hidden", + "isDefault" + ] }, "TextAreaFieldOutput_2024_06_14": { "type": "object", @@ -16084,7 +17290,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "TextFieldOutput_2024_06_14": { "type": "object", @@ -16141,7 +17354,14 @@ "example": false } }, - "required": ["type", "slug", "label", "required", "hidden", "isDefault"] + "required": [ + "type", + "slug", + "label", + "required", + "hidden", + "isDefault" + ] }, "EventTypeOutput_2024_06_14": { "type": "object", @@ -16155,7 +17375,11 @@ "example": 60 }, "lengthInMinutesOptions": { - "example": [15, 30, 60], + "example": [ + 15, + 30, + 60 + ], "description": "If you want that user can choose between different lengths of the event you can specify them here. Must include the provided `lengthInMinutes`.", "type": "array", "items": { @@ -16435,21 +17659,30 @@ "properties": { "status": { "type": "string", - "enum": ["success", "error"], + "enum": [ + "success", + "error" + ], "example": "success" }, "data": { "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetEventTypeOutput_2024_06_14": { "type": "object", "properties": { "status": { "type": "string", - "enum": ["success", "error"], + "enum": [ + "success", + "error" + ], "example": "success" }, "data": { @@ -16461,14 +17694,20 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetEventTypesOutput_2024_06_14": { "type": "object", "properties": { "status": { "type": "string", - "enum": ["success", "error"], + "enum": [ + "success", + "error" + ], "example": "success" }, "data": { @@ -16478,7 +17717,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateEventTypeInput_2024_06_14": { "type": "object", @@ -16488,7 +17730,11 @@ "example": 60 }, "lengthInMinutesOptions": { - "example": [15, 30, 60], + "example": [ + 15, + 30, + 60 + ], "description": "If you want that user can choose between different lengths of the event you can specify them here. Must include the provided `lengthInMinutes`.", "type": "array", "items": { @@ -16509,7 +17755,7 @@ }, "bookingFields": { "type": "array", - "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "description": "Complete set of booking form fields. This array replaces all existing booking fields. To modify existing fields, first fetch the current event type, then include all desired fields in this array. Sending only one field will remove all other custom fields, keeping only default fields plus the provided one.", "items": { "oneOf": [ { @@ -16760,14 +18006,20 @@ "properties": { "status": { "type": "string", - "enum": ["success", "error"], + "enum": [ + "success", + "error" + ], "example": "success" }, "data": { "$ref": "#/components/schemas/EventTypeOutput_2024_06_14" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteData_2024_06_14": { "type": "object", @@ -16788,21 +18040,32 @@ "type": "string" } }, - "required": ["id", "lengthInMinutes", "title", "slug"] + "required": [ + "id", + "lengthInMinutes", + "title", + "slug" + ] }, "DeleteEventTypeOutput_2024_06_14": { "type": "object", "properties": { "status": { "type": "string", - "enum": ["success", "error"], + "enum": [ + "success", + "error" + ], "example": "success" }, "data": { "$ref": "#/components/schemas/DeleteData_2024_06_14" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "SelectedCalendarsInputDto": { "type": "object", @@ -16820,7 +18083,11 @@ "type": "string" } }, - "required": ["integration", "externalId", "credentialId"] + "required": [ + "integration", + "externalId", + "credentialId" + ] }, "SelectedCalendarOutputDto": { "type": "object", @@ -16839,7 +18106,12 @@ "nullable": true } }, - "required": ["userId", "integration", "externalId", "credentialId"] + "required": [ + "userId", + "integration", + "externalId", + "credentialId" + ] }, "SelectedCalendarOutputResponseDto": { "type": "object", @@ -16847,13 +18119,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/SelectedCalendarOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OrgTeamOutputDto": { "type": "object", @@ -16929,7 +18207,11 @@ "default": "Sunday" } }, - "required": ["id", "name", "isOrganization"] + "required": [ + "id", + "name", + "isOrganization" + ] }, "OrgTeamsOutputResponseDto": { "type": "object", @@ -16937,7 +18219,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -16946,7 +18231,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OrgMeTeamsOutputResponseDto": { "type": "object", @@ -16954,7 +18242,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -16963,7 +18254,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OrgTeamOutputResponseDto": { "type": "object", @@ -16971,13 +18265,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OrgTeamOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOrgTeamDto": { "type": "object", @@ -17142,19 +18442,40 @@ "description": "If you are a platform customer, don't pass 'false', because then team creator won't be able to create team event types." } }, - "required": ["name"] + "required": [ + "name" + ] }, "ScheduleAvailabilityInput_2024_06_11": { "type": "object", "properties": { "days": { "type": "array", - "enum": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], - "example": ["Monday", "Tuesday"], + "enum": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ], + "example": [ + "Monday", + "Tuesday" + ], "description": "Array of days when schedule is active.", "items": { "type": "string", - "enum": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + "enum": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ] } }, "startTime": { @@ -17170,7 +18491,11 @@ "description": "endTime must be a valid time in format HH:MM e.g. 15:00" } }, - "required": ["days", "startTime", "endTime"] + "required": [ + "days", + "startTime", + "endTime" + ] }, "ScheduleOverrideInput_2024_06_11": { "type": "object", @@ -17192,7 +18517,11 @@ "description": "endTime must be a valid time in format HH:MM e.g. 13:00" } }, - "required": ["date", "startTime", "endTime"] + "required": [ + "date", + "startTime", + "endTime" + ] }, "ScheduleOutput_2024_06_11": { "type": "object", @@ -17216,12 +18545,18 @@ "availability": { "example": [ { - "days": ["Monday", "Tuesday"], + "days": [ + "Monday", + "Tuesday" + ], "startTime": "17:00", "endTime": "19:00" }, { - "days": ["Wednesday", "Thursday"], + "days": [ + "Wednesday", + "Thursday" + ], "startTime": "16:00", "endTime": "20:00" } @@ -17249,7 +18584,15 @@ } } }, - "required": ["id", "ownerId", "name", "timeZone", "availability", "isDefault", "overrides"] + "required": [ + "id", + "ownerId", + "name", + "timeZone", + "availability", + "isDefault", + "overrides" + ] }, "GetSchedulesOutput_2024_06_11": { "type": "object", @@ -17257,7 +18600,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -17269,7 +18615,10 @@ "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateScheduleInput_2024_06_11": { "type": "object", @@ -17287,12 +18636,18 @@ "description": "Each object contains days and times when the user is available. If not passed, the default availability is Monday to Friday from 09:00 to 17:00.", "example": [ { - "days": ["Monday", "Tuesday"], + "days": [ + "Monday", + "Tuesday" + ], "startTime": "17:00", "endTime": "19:00" }, { - "days": ["Wednesday", "Thursday"], + "days": [ + "Wednesday", + "Thursday" + ], "startTime": "16:00", "endTime": "20:00" } @@ -17322,7 +18677,11 @@ } } }, - "required": ["name", "timeZone", "isDefault"] + "required": [ + "name", + "timeZone", + "isDefault" + ] }, "CreateScheduleOutput_2024_06_11": { "type": "object", @@ -17330,13 +18689,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetScheduleOutput_2024_06_11": { "type": "object", @@ -17344,7 +18709,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "nullable": true, @@ -17358,7 +18726,10 @@ "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateScheduleInput_2024_06_11": { "type": "object", @@ -17374,7 +18745,10 @@ "availability": { "example": [ { - "days": ["Monday", "Tuesday"], + "days": [ + "Monday", + "Tuesday" + ], "startTime": "09:00", "endTime": "10:00" } @@ -17409,7 +18783,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" @@ -17418,7 +18795,10 @@ "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteScheduleOutput_2024_06_11": { "type": "object", @@ -17426,10 +18806,15 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] } }, - "required": ["status"] + "required": [ + "status" + ] }, "ProfileOutput": { "type": "object", @@ -17456,7 +18841,11 @@ "example": "john_doe" } }, - "required": ["id", "organizationId", "userId"] + "required": [ + "id", + "organizationId", + "userId" + ] }, "GetOrgUsersWithProfileOutput": { "type": "object", @@ -17598,7 +18987,15 @@ ] } }, - "required": ["id", "email", "timeZone", "weekStart", "hideBranding", "createdDate", "profile"] + "required": [ + "id", + "email", + "timeZone", + "weekStart", + "hideBranding", + "createdDate", + "profile" + ] }, "GetOrganizationUsersResponseDTO": { "type": "object", @@ -17606,7 +19003,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -17615,7 +19015,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateOrganizationUserInput": { "type": "object", @@ -17705,14 +19108,20 @@ "organizationRole": { "type": "string", "default": "MEMBER", - "enum": ["MEMBER", "ADMIN", "OWNER"] + "enum": [ + "MEMBER", + "ADMIN", + "OWNER" + ] }, "autoAccept": { "type": "boolean", "default": true } }, - "required": ["email"] + "required": [ + "email" + ] }, "GetOrganizationUserOutput": { "type": "object", @@ -17720,13 +19129,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/GetOrgUsersWithProfileOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOrganizationUserInput": { "type": "object", @@ -17742,7 +19157,10 @@ "type": "string" } }, - "required": ["id", "name"] + "required": [ + "id", + "name" + ] }, "TextAttribute": { "type": "object", @@ -17763,7 +19181,13 @@ "type": "string" } }, - "required": ["id", "name", "type", "option", "optionId"] + "required": [ + "id", + "name", + "type", + "option", + "optionId" + ] }, "NumberAttribute": { "type": "object", @@ -17784,7 +19208,13 @@ "type": "string" } }, - "required": ["id", "name", "type", "option", "optionId"] + "required": [ + "id", + "name", + "type", + "option", + "optionId" + ] }, "SingleSelectAttribute": { "type": "object", @@ -17805,7 +19235,13 @@ "type": "string" } }, - "required": ["id", "name", "type", "option", "optionId"] + "required": [ + "id", + "name", + "type", + "option", + "optionId" + ] }, "MultiSelectAttributeOption": { "type": "object", @@ -17817,7 +19253,10 @@ "type": "string" } }, - "required": ["optionId", "option"] + "required": [ + "optionId", + "option" + ] }, "MultiSelectAttribute": { "type": "object", @@ -17838,7 +19277,12 @@ } } }, - "required": ["id", "name", "type", "options"] + "required": [ + "id", + "name", + "type", + "options" + ] }, "MembershipUserOutputDto": { "type": "object", @@ -17865,7 +19309,9 @@ } } }, - "required": ["email"] + "required": [ + "email" + ] }, "OrganizationMembershipOutput": { "type": "object", @@ -17884,7 +19330,11 @@ }, "role": { "type": "string", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean" @@ -17912,7 +19362,15 @@ } } }, - "required": ["id", "userId", "teamId", "accepted", "role", "user", "attributes"] + "required": [ + "id", + "userId", + "teamId", + "accepted", + "role", + "user", + "attributes" + ] }, "GetAllOrgMemberships": { "type": "object", @@ -17920,13 +19378,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OrganizationMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateOrgMembershipDto": { "type": "object", @@ -17941,7 +19405,11 @@ "role": { "type": "string", "default": "MEMBER", - "enum": ["MEMBER", "OWNER", "ADMIN"], + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ], "description": "If you are platform customer then managed users should only have MEMBER role." }, "disableImpersonation": { @@ -17949,7 +19417,10 @@ "default": false } }, - "required": ["userId", "role"] + "required": [ + "userId", + "role" + ] }, "CreateOrgMembershipOutput": { "type": "object", @@ -17957,13 +19428,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OrganizationMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetOrgMembership": { "type": "object", @@ -17971,13 +19448,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OrganizationMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteOrgMembership": { "type": "object", @@ -17985,13 +19468,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OrganizationMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOrgMembershipDto": { "type": "object", @@ -18001,7 +19490,11 @@ }, "role": { "type": "string", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean" @@ -18014,13 +19507,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OrganizationMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "Host": { "type": "object", @@ -18035,10 +19534,18 @@ }, "priority": { "type": "string", - "enum": ["lowest", "low", "medium", "high", "highest"] + "enum": [ + "lowest", + "low", + "medium", + "high", + "highest" + ] } }, - "required": ["userId"] + "required": [ + "userId" + ] }, "CreateTeamEventTypeInput_2024_06_14": { "type": "object", @@ -18048,7 +19555,11 @@ "example": 60 }, "lengthInMinutesOptions": { - "example": [15, 30, 60], + "example": [ + 15, + 30, + 60 + ], "description": "If you want that user can choose between different lengths of the event you can specify them here. Must include the provided `lengthInMinutes`.", "type": "array", "items": { @@ -18289,7 +19800,11 @@ }, "schedulingType": { "type": "string", - "enum": ["collective", "roundRobin", "managed"], + "enum": [ + "collective", + "roundRobin", + "managed" + ], "example": "collective", "description": "The scheduling type for the team event - collective, roundRobin or managed." }, @@ -18337,7 +19852,12 @@ } } }, - "required": ["lengthInMinutes", "title", "slug", "schedulingType"] + "required": [ + "lengthInMinutes", + "title", + "slug", + "schedulingType" + ] }, "CreateTeamEventTypeOutput": { "type": "object", @@ -18345,7 +19865,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -18361,7 +19884,10 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamEventTypeResponseHost": { "type": "object", @@ -18378,7 +19904,13 @@ "priority": { "type": "string", "default": "medium", - "enum": ["lowest", "low", "medium", "high", "highest"] + "enum": [ + "lowest", + "low", + "medium", + "high", + "highest" + ] }, "name": { "type": "string", @@ -18394,7 +19926,11 @@ "example": "https://cal.com/api/avatar/d95949bc-ccb1-400f-acf6-045c51a16856.png" } }, - "required": ["userId", "name", "username"] + "required": [ + "userId", + "name", + "username" + ] }, "EventTypeTeam": { "type": "object", @@ -18427,7 +19963,9 @@ "type": "string" } }, - "required": ["id"] + "required": [ + "id" + ] }, "TeamEventTypeOutput_2024_06_14": { "type": "object", @@ -18442,7 +19980,11 @@ "example": 60 }, "lengthInMinutesOptions": { - "example": [15, 30, 60], + "example": [ + 15, + 30, + 60 + ], "description": "If you want that user can choose between different lengths of the event you can specify them here. Must include the provided `lengthInMinutes`.", "type": "array", "items": { @@ -18713,7 +20255,11 @@ }, "schedulingType": { "type": "string", - "enum": ["roundRobin", "collective", "managed"] + "enum": [ + "roundRobin", + "collective", + "managed" + ] }, "team": { "$ref": "#/components/schemas/EventTypeTeam" @@ -18749,13 +20295,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamEventTypeOutput_2024_06_14" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreatePhoneCallInput": { "type": "object", @@ -18781,7 +20333,10 @@ }, "templateType": { "default": "CUSTOM_TEMPLATE", - "enum": ["CHECK_IN_APPOINTMENT", "CUSTOM_TEMPLATE"], + "enum": [ + "CHECK_IN_APPOINTMENT", + "CUSTOM_TEMPLATE" + ], "type": "string", "description": "Template type" }, @@ -18810,7 +20365,13 @@ "description": "General prompt" } }, - "required": ["yourPhoneNumber", "numberToCall", "calApiKey", "enabled", "templateType"] + "required": [ + "yourPhoneNumber", + "numberToCall", + "calApiKey", + "enabled", + "templateType" + ] }, "Data": { "type": "object", @@ -18822,7 +20383,9 @@ "type": "string" } }, - "required": ["callId"] + "required": [ + "callId" + ] }, "CreatePhoneCallOutput": { "type": "object", @@ -18830,13 +20393,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/Data" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetTeamEventTypesOutput": { "type": "object", @@ -18844,7 +20413,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -18853,7 +20425,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateTeamEventTypeInput_2024_06_14": { "type": "object", @@ -18863,7 +20438,11 @@ "example": 60 }, "lengthInMinutesOptions": { - "example": [15, 30, 60], + "example": [ + 15, + 30, + 60 + ], "description": "If you want that user can choose between different lengths of the event you can specify them here. Must include the provided `lengthInMinutes`.", "type": "array", "items": { @@ -18884,7 +20463,7 @@ }, "bookingFields": { "type": "array", - "description": "Custom fields that can be added to the booking form when the event is booked by someone. By default booking form has name and email field.", + "description": "Complete set of booking form fields. This array replaces all existing booking fields. To modify existing fields, first fetch the current event type, then include all desired fields in this array. Sending only one field will remove all other custom fields, keeping only default fields plus the provided one.", "items": { "oneOf": [ { @@ -19149,7 +20728,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -19165,7 +20747,10 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteTeamEventTypeOutput": { "type": "object", @@ -19173,13 +20758,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamMembershipOutput": { "type": "object", @@ -19198,7 +20789,11 @@ }, "role": { "type": "string", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean" @@ -19207,7 +20802,14 @@ "$ref": "#/components/schemas/MembershipUserOutputDto" } }, - "required": ["id", "userId", "teamId", "accepted", "role", "user"] + "required": [ + "id", + "userId", + "teamId", + "accepted", + "role", + "user" + ] }, "OrgTeamMembershipsOutputResponseDto": { "type": "object", @@ -19215,7 +20817,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -19224,7 +20829,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OrgTeamMembershipOutputResponseDto": { "type": "object", @@ -19232,13 +20840,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOrgTeamMembershipDto": { "type": "object", @@ -19248,7 +20862,11 @@ }, "role": { "type": "string", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean" @@ -19268,14 +20886,21 @@ "role": { "type": "string", "default": "MEMBER", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean", "default": false } }, - "required": ["userId", "role"] + "required": [ + "userId", + "role" + ] }, "Attribute": { "type": "object", @@ -19293,7 +20918,12 @@ "type": { "type": "string", "description": "The type of the attribute", - "enum": ["TEXT", "NUMBER", "SINGLE_SELECT", "MULTI_SELECT"] + "enum": [ + "TEXT", + "NUMBER", + "SINGLE_SELECT", + "MULTI_SELECT" + ] }, "name": { "type": "string", @@ -19316,7 +20946,14 @@ "example": true } }, - "required": ["id", "teamId", "type", "name", "slug", "enabled"] + "required": [ + "id", + "teamId", + "type", + "name", + "slug", + "enabled" + ] }, "GetOrganizationAttributesOutput": { "type": "object", @@ -19324,7 +20961,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -19333,7 +20973,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetSingleAttributeOutput": { "type": "object", @@ -19341,7 +20984,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "nullable": true, @@ -19352,7 +20998,10 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateOrganizationAttributeOptionInput": { "type": "object", @@ -19364,7 +21013,10 @@ "type": "string" } }, - "required": ["value", "slug"] + "required": [ + "value", + "slug" + ] }, "CreateOrganizationAttributeInput": { "type": "object", @@ -19377,7 +21029,12 @@ }, "type": { "type": "string", - "enum": ["TEXT", "NUMBER", "SINGLE_SELECT", "MULTI_SELECT"] + "enum": [ + "TEXT", + "NUMBER", + "SINGLE_SELECT", + "MULTI_SELECT" + ] }, "options": { "type": "array", @@ -19389,7 +21046,12 @@ "type": "boolean" } }, - "required": ["name", "slug", "type", "options"] + "required": [ + "name", + "slug", + "type", + "options" + ] }, "CreateOrganizationAttributesOutput": { "type": "object", @@ -19397,13 +21059,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/Attribute" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOrganizationAttributeInput": { "type": "object", @@ -19416,7 +21084,12 @@ }, "type": { "type": "string", - "enum": ["TEXT", "NUMBER", "SINGLE_SELECT", "MULTI_SELECT"] + "enum": [ + "TEXT", + "NUMBER", + "SINGLE_SELECT", + "MULTI_SELECT" + ] }, "enabled": { "type": "boolean" @@ -19429,13 +21102,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/Attribute" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteOrganizationAttributesOutput": { "type": "object", @@ -19443,13 +21122,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/Attribute" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OptionOutput": { "type": "object", @@ -19475,7 +21160,12 @@ "example": "option-slug" } }, - "required": ["id", "attributeId", "value", "slug"] + "required": [ + "id", + "attributeId", + "value", + "slug" + ] }, "CreateAttributeOptionOutput": { "type": "object", @@ -19483,13 +21173,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OptionOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteAttributeOptionOutput": { "type": "object", @@ -19497,13 +21193,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OptionOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateOrganizationAttributeOptionInput": { "type": "object", @@ -19522,13 +21224,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OptionOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetAllAttributeOptionOutput": { "type": "object", @@ -19536,7 +21244,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -19545,7 +21256,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "AssignedOptionOutput": { "type": "object", @@ -19572,14 +21286,23 @@ }, "assignedUserIds": { "description": "Ids of the users assigned to the attribute option.", - "example": [124, 224], + "example": [ + 124, + 224 + ], "type": "array", "items": { "type": "string" } } }, - "required": ["id", "attributeId", "value", "slug", "assignedUserIds"] + "required": [ + "id", + "attributeId", + "value", + "slug", + "assignedUserIds" + ] }, "GetAllAttributeAssignedOptionOutput": { "type": "object", @@ -19587,7 +21310,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -19596,7 +21322,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "AssignOrganizationAttributeOptionToUserInput": { "type": "object", @@ -19611,7 +21340,9 @@ "type": "string" } }, - "required": ["attributeId"] + "required": [ + "attributeId" + ] }, "AssignOptionUserOutputData": { "type": "object", @@ -19629,7 +21360,11 @@ "description": "The value of the option" } }, - "required": ["id", "memberId", "attributeOptionId"] + "required": [ + "id", + "memberId", + "attributeOptionId" + ] }, "AssignOptionUserOutput": { "type": "object", @@ -19637,13 +21372,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/AssignOptionUserOutputData" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UnassignOptionUserOutput": { "type": "object", @@ -19651,13 +21392,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/AssignOptionUserOutputData" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetOptionUserOutputData": { "type": "object", @@ -19679,7 +21426,12 @@ "description": "The slug of the option" } }, - "required": ["id", "attributeId", "value", "slug"] + "required": [ + "id", + "attributeId", + "value", + "slug" + ] }, "GetOptionUserOutput": { "type": "object", @@ -19687,7 +21439,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -19696,7 +21451,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamWebhookOutputDto": { "type": "object", @@ -19728,7 +21486,14 @@ "type": "string" } }, - "required": ["payloadTemplate", "teamId", "id", "triggers", "subscriberUrl", "active"] + "required": [ + "payloadTemplate", + "teamId", + "id", + "triggers", + "subscriberUrl", + "active" + ] }, "TeamWebhooksOutputResponseDto": { "type": "object", @@ -19736,7 +21501,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -19745,7 +21513,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateWebhookInputDto": { "type": "object", @@ -19798,7 +21569,11 @@ "type": "string" } }, - "required": ["active", "subscriberUrl", "triggers"] + "required": [ + "active", + "subscriberUrl", + "triggers" + ] }, "TeamWebhookOutputResponseDto": { "type": "object", @@ -19806,13 +21581,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamWebhookOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateWebhookInputDto": { "type": "object", @@ -19895,10 +21676,19 @@ "type": "string", "description": "the reason for the out of office entry, if applicable", "example": "vacation", - "enum": ["unspecified", "vacation", "travel", "sick", "public_holiday"] + "enum": [ + "unspecified", + "vacation", + "travel", + "sick", + "public_holiday" + ] } }, - "required": ["start", "end"] + "required": [ + "start", + "end" + ] }, "UpdateOutOfOfficeEntryDto": { "type": "object", @@ -19929,7 +21719,13 @@ "type": "string", "description": "the reason for the out of office entry, if applicable", "example": "vacation", - "enum": ["unspecified", "vacation", "travel", "sick", "public_holiday"] + "enum": [ + "unspecified", + "vacation", + "travel", + "sick", + "public_holiday" + ] } } }, @@ -19944,7 +21740,10 @@ }, "activeOnEventTypeIds": { "description": "List of Event Type IDs the workflow is specifically active on (if not active on all)", - "example": [698191, 698192], + "example": [ + 698191, + 698192 + ], "type": "array", "items": { "type": "number" @@ -19964,10 +21763,17 @@ "type": "string", "description": "Unit for the offset time", "example": "hour", - "enum": ["hour", "minute", "day"] + "enum": [ + "hour", + "minute", + "day" + ] } }, - "required": ["value", "unit"] + "required": [ + "value", + "unit" + ] }, "WorkflowTriggerOutputDto": { "type": "object", @@ -19995,7 +21801,9 @@ ] } }, - "required": ["type"] + "required": [ + "type" + ] }, "WorkflowMessageOutputDto": { "type": "object", @@ -20016,7 +21824,9 @@ "example": "Reminder for {EVENT_NAME}." } }, - "required": ["subject"] + "required": [ + "subject" + ] }, "WorkflowStepOutputDto": { "type": "object", @@ -20049,7 +21859,12 @@ "type": "string", "description": "Intended recipient type", "example": "const", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "email": { "type": "string", @@ -20064,7 +21879,14 @@ "type": "string", "description": "Template type used", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "includeCalendarEvent": { "type": "object", @@ -20086,7 +21908,15 @@ ] } }, - "required": ["id", "stepNumber", "action", "recipient", "template", "sender", "message"] + "required": [ + "id", + "stepNumber", + "action", + "recipient", + "template", + "sender", + "message" + ] }, "WorkflowOutput": { "type": "object", @@ -20145,7 +21975,13 @@ "example": "2024-05-12T11:30:00.000Z" } }, - "required": ["id", "name", "activation", "trigger", "steps"] + "required": [ + "id", + "name", + "activation", + "trigger", + "steps" + ] }, "GetWorkflowsOutput": { "type": "object", @@ -20154,7 +21990,10 @@ "type": "string", "description": "Indicates the status of the response", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "description": "List of workflows", @@ -20164,7 +22003,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetWorkflowOutput": { "type": "object", @@ -20173,7 +22015,10 @@ "type": "string", "description": "Indicates the status of the response", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "description": "workflow", @@ -20183,7 +22028,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "WorkflowTriggerOffsetDto": { "type": "object", @@ -20199,7 +22047,10 @@ "example": "hour" } }, - "required": ["value", "unit"] + "required": [ + "value", + "unit" + ] }, "OnBeforeEventTriggerDto": { "type": "object", @@ -20219,7 +22070,10 @@ "example": "beforeEvent" } }, - "required": ["offset", "type"] + "required": [ + "offset", + "type" + ] }, "OnAfterEventTriggerDto": { "type": "object", @@ -20239,7 +22093,10 @@ "example": "afterEvent" } }, - "required": ["offset", "type"] + "required": [ + "offset", + "type" + ] }, "OnCancelTriggerDto": { "type": "object", @@ -20250,7 +22107,9 @@ "description": "Trigger type for the workflow" } }, - "required": ["type"] + "required": [ + "type" + ] }, "OnCreationTriggerDto": { "type": "object", @@ -20261,7 +22120,9 @@ "description": "Trigger type for the workflow" } }, - "required": ["type"] + "required": [ + "type" + ] }, "OnRescheduleTriggerDto": { "type": "object", @@ -20272,7 +22133,9 @@ "description": "Trigger type for the workflow" } }, - "required": ["type"] + "required": [ + "type" + ] }, "OnAfterCalVideoGuestsNoShowTriggerDto": { "type": "object", @@ -20292,7 +22155,10 @@ "example": "afterGuestsCalVideoNoShow" } }, - "required": ["offset", "type"] + "required": [ + "offset", + "type" + ] }, "OnAfterCalVideoHostsNoShowTriggerDto": { "type": "object", @@ -20312,7 +22178,10 @@ "example": "afterHostsCalVideoNoShow" } }, - "required": ["offset", "type"] + "required": [ + "offset", + "type" + ] }, "HtmlWorkflowMessageDto": { "type": "object", @@ -20328,7 +22197,10 @@ "example": "

This is a reminder from {ORGANIZER} of {EVENT_NAME} to {ATTENDEE} starting here {LOCATION} {MEETING_URL} at {START_TIME_h:mma} {TIMEZONE}.

" } }, - "required": ["subject", "html"] + "required": [ + "subject", + "html" + ] }, "WorkflowEmailAddressStepDto": { "type": "object", @@ -20357,13 +22229,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20430,13 +22314,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20494,13 +22390,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20545,7 +22453,10 @@ "example": "This is a reminder message from {ORGANIZER} of {EVENT_NAME} to {ATTENDEE} starting here {LOCATION} {MEETING_URL} at {START_TIME_h:mma} {TIMEZONE}." } }, - "required": ["subject", "text"] + "required": [ + "subject", + "text" + ] }, "WorkflowPhoneWhatsAppAttendeeStepDto": { "type": "object", @@ -20574,13 +22485,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20595,7 +22518,14 @@ ] } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "message" + ] }, "WorkflowPhoneWhatsAppNumberStepDto": { "type": "object", @@ -20624,13 +22554,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20653,7 +22595,15 @@ ] } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "verifiedPhoneId", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "verifiedPhoneId", + "message" + ] }, "WorkflowPhoneNumberStepDto": { "type": "object", @@ -20682,13 +22632,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20711,7 +22673,15 @@ ] } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "verifiedPhoneId", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "verifiedPhoneId", + "message" + ] }, "WorkflowPhoneAttendeeStepDto": { "type": "object", @@ -20740,13 +22710,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20765,7 +22747,14 @@ ] } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "message" + ] }, "BaseWorkflowTriggerDto": { "type": "object", @@ -20775,7 +22764,9 @@ "description": "Trigger type for the workflow" } }, - "required": ["type"] + "required": [ + "type" + ] }, "WorkflowActivationDto": { "type": "object", @@ -20789,14 +22780,18 @@ "activeOnEventTypeIds": { "default": [], "description": "List of event-types IDs the workflow applies to, required if isActiveOnAllEventTypes is false", - "example": [698191], + "example": [ + 698191 + ], "type": "array", "items": { "type": "number" } } }, - "required": ["isActiveOnAllEventTypes"] + "required": [ + "isActiveOnAllEventTypes" + ] }, "CreateWorkflowDto": { "type": "object", @@ -20870,7 +22865,12 @@ } } }, - "required": ["name", "activation", "trigger", "steps"] + "required": [ + "name", + "activation", + "trigger", + "steps" + ] }, "UpdateEmailAddressWorkflowStepDto": { "type": "object", @@ -20899,13 +22899,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -20977,13 +22989,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -21046,13 +23070,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -21115,13 +23151,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -21145,7 +23193,14 @@ "example": 67244 } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "message" + ] }, "UpdatePhoneWhatsAppNumberWorkflowStepDto": { "type": "object", @@ -21174,13 +23229,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -21208,7 +23275,15 @@ "example": 67244 } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "verifiedPhoneId", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "verifiedPhoneId", + "message" + ] }, "UpdateWhatsAppAttendeePhoneWorkflowStepDto": { "type": "object", @@ -21237,13 +23312,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -21263,7 +23350,14 @@ "example": 67244 } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "message" + ] }, "UpdatePhoneNumberWorkflowStepDto": { "type": "object", @@ -21292,13 +23386,25 @@ "type": "string", "description": "Recipient type", "example": "attendee", - "enum": ["const", "attendee", "email", "phone_number"] + "enum": [ + "const", + "attendee", + "email", + "phone_number" + ] }, "template": { "type": "string", "description": "Template type for the step", "example": "reminder", - "enum": ["reminder", "custom", "rescheduled", "completed", "rating", "cancelled"] + "enum": [ + "reminder", + "custom", + "rescheduled", + "completed", + "rating", + "cancelled" + ] }, "sender": { "type": "string", @@ -21326,7 +23432,15 @@ "example": 67244 } }, - "required": ["action", "stepNumber", "recipient", "template", "sender", "verifiedPhoneId", "message"] + "required": [ + "action", + "stepNumber", + "recipient", + "template", + "sender", + "verifiedPhoneId", + "message" + ] }, "UpdateWorkflowDto": { "type": "object", @@ -21408,7 +23522,9 @@ "type": "string" } }, - "required": ["authUrl"] + "required": [ + "authUrl" + ] }, "StripConnectOutputResponseDto": { "type": "object", @@ -21416,13 +23532,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/StripConnectOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "StripCredentialsSaveOutputResponseDto": { "type": "object", @@ -21431,7 +23553,9 @@ "type": "string" } }, - "required": ["url"] + "required": [ + "url" + ] }, "StripCredentialsCheckOutputResponseDto": { "type": "object", @@ -21441,7 +23565,9 @@ "example": "success" } }, - "required": ["status"] + "required": [ + "status" + ] }, "GetDefaultScheduleOutput_2024_06_11": { "type": "object", @@ -21449,13 +23575,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput_2024_06_11" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateTeamInput": { "type": "object", @@ -21539,7 +23671,9 @@ "description": "If you are a platform customer, don't pass 'false', because then team creator won't be able to create team event types." } }, - "required": ["name"] + "required": [ + "name" + ] }, "CreateTeamOutput": { "type": "object", @@ -21547,7 +23681,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -21561,7 +23698,10 @@ "description": "Either an Output object or a TeamOutputDto." } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamOutputDto": { "type": "object", @@ -21637,7 +23777,11 @@ "default": "Sunday" } }, - "required": ["id", "name", "isOrganization"] + "required": [ + "id", + "name", + "isOrganization" + ] }, "GetTeamOutput": { "type": "object", @@ -21645,13 +23789,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetTeamsOutput": { "type": "object", @@ -21659,7 +23809,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -21668,7 +23821,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateTeamOutput": { "type": "object", @@ -21676,13 +23832,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "ConferencingAppsOutputDto": { "type": "object", @@ -21707,20 +23869,30 @@ "description": "Whether if the connection is working or not." } }, - "required": ["id", "type", "userId"] + "required": [ + "id", + "type", + "userId" + ] }, "ConferencingAppOutputResponseDto": { "type": "object", "properties": { "status": { "type": "string", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ConferencingAppsOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetConferencingAppsOauthUrlResponseDto": { "type": "object", @@ -21728,17 +23900,25 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] } }, - "required": ["status"] + "required": [ + "status" + ] }, "ConferencingAppsOutputResponseDto": { "type": "object", "properties": { "status": { "type": "string", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -21747,7 +23927,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "SetDefaultConferencingAppOutputResponseDto": { "type": "object", @@ -21755,10 +23938,15 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] } }, - "required": ["status"] + "required": [ + "status" + ] }, "DefaultConferencingAppsOutputDto": { "type": "object", @@ -21777,13 +23965,18 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/DefaultConferencingAppsOutputDto" } }, - "required": ["status"] + "required": [ + "status" + ] }, "DisconnectConferencingAppOutputResponseDto": { "type": "object", @@ -21791,10 +23984,15 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] } }, - "required": ["status"] + "required": [ + "status" + ] }, "GoogleServiceAccountKeyInput": { "type": "object", @@ -21809,7 +24007,11 @@ "type": "string" } }, - "required": ["private_key", "client_email", "client_id"] + "required": [ + "private_key", + "client_email", + "client_id" + ] }, "CreateDelegationCredentialInput": { "type": "object", @@ -21834,7 +24036,11 @@ } } }, - "required": ["workspacePlatformSlug", "domain", "serviceAccountKey"] + "required": [ + "workspacePlatformSlug", + "domain", + "serviceAccountKey" + ] }, "WorkspacePlatformDto": { "type": "object", @@ -21846,7 +24052,10 @@ "type": "string" } }, - "required": ["name", "slug"] + "required": [ + "name", + "slug" + ] }, "DelegationCredentialOutput": { "type": "object", @@ -21891,13 +24100,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/DelegationCredentialOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateDelegationCredentialInput": { "type": "object", @@ -21926,20 +24141,29 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/DelegationCredentialOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateIcsFeedInputDto": { "type": "object", "properties": { "urls": { "type": "array", - "example": ["https://cal.com/ics/feed.ics", "http://cal.com/ics/feed.ics"], + "example": [ + "https://cal.com/ics/feed.ics", + "http://cal.com/ics/feed.ics" + ], "description": "An array of ICS URLs", "items": { "type": "string", @@ -21953,7 +24177,9 @@ "description": "Whether to allowing writing to the calendar or not" } }, - "required": ["urls"] + "required": [ + "urls" + ] }, "CreateIcsFeedOutput": { "type": "object", @@ -21993,7 +24219,14 @@ "description": "Whether the calendar credentials are valid or not" } }, - "required": ["id", "type", "userId", "teamId", "appId", "invalid"] + "required": [ + "id", + "type", + "userId", + "teamId", + "appId", + "invalid" + ] }, "CreateIcsFeedOutputResponseDto": { "type": "object", @@ -22001,13 +24234,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/CreateIcsFeedOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "BusyTimesOutput": { "type": "object", @@ -22025,7 +24264,10 @@ "nullable": true } }, - "required": ["start", "end"] + "required": [ + "start", + "end" + ] }, "GetBusyTimesOutput": { "type": "object", @@ -22033,7 +24275,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -22042,7 +24287,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "Integration": { "type": "object", @@ -22151,7 +24399,13 @@ "nullable": true } }, - "required": ["externalId", "primary", "readOnly", "isSelected", "credentialId"] + "required": [ + "externalId", + "primary", + "readOnly", + "isSelected", + "credentialId" + ] }, "Calendar": { "type": "object", @@ -22186,7 +24440,12 @@ "nullable": true } }, - "required": ["externalId", "readOnly", "isSelected", "credentialId"] + "required": [ + "externalId", + "readOnly", + "isSelected", + "credentialId" + ] }, "ConnectedCalendar": { "type": "object", @@ -22211,7 +24470,10 @@ } } }, - "required": ["integration", "credentialId"] + "required": [ + "integration", + "credentialId" + ] }, "DestinationCalendar": { "type": "object", @@ -22285,7 +24547,10 @@ "$ref": "#/components/schemas/DestinationCalendar" } }, - "required": ["connectedCalendars", "destinationCalendar"] + "required": [ + "connectedCalendars", + "destinationCalendar" + ] }, "ConnectedCalendarsOutput": { "type": "object", @@ -22293,13 +24558,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ConnectedCalendarsData" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateCalendarCredentialsInput": { "type": "object", @@ -22311,7 +24582,10 @@ "type": "string" } }, - "required": ["username", "password"] + "required": [ + "username", + "password" + ] }, "DeleteCalendarCredentialsInputBodyDto": { "type": "object", @@ -22322,7 +24596,9 @@ "description": "Credential ID of the calendar to delete, as returned by the /calendars endpoint" } }, - "required": ["id"] + "required": [ + "id" + ] }, "DeletedCalendarCredentialsOutputDto": { "type": "object", @@ -22350,7 +24626,14 @@ "nullable": true } }, - "required": ["id", "type", "userId", "teamId", "appId", "invalid"] + "required": [ + "id", + "type", + "userId", + "teamId", + "appId", + "invalid" + ] }, "DeletedCalendarCredentialsOutputResponseDto": { "type": "object", @@ -22358,14 +24641,20 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/DeletedCalendarCredentialsOutputDto" } }, - "required": ["status", "data"] - }, + "required": [ + "status", + "data" + ] + }, "CreateOrganizationInput": { "type": "object", "properties": { @@ -22400,7 +24689,9 @@ } } }, - "required": ["name"] + "required": [ + "name" + ] }, "ManagedOrganizationWithApiKeyOutput": { "type": "object", @@ -22425,7 +24716,11 @@ "type": "string" } }, - "required": ["id", "name", "apiKey"] + "required": [ + "id", + "name", + "apiKey" + ] }, "CreateManagedOrganizationOutput": { "type": "object", @@ -22433,13 +24728,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ManagedOrganizationWithApiKeyOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "ManagedOrganizationOutput": { "type": "object", @@ -22461,7 +24762,10 @@ } } }, - "required": ["id", "name"] + "required": [ + "id", + "name" + ] }, "GetManagedOrganizationOutput": { "type": "object", @@ -22469,13 +24773,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ManagedOrganizationOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "PaginationMetaDto": { "type": "object", @@ -22543,7 +24853,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -22555,7 +24868,11 @@ "$ref": "#/components/schemas/PaginationMetaDto" } }, - "required": ["status", "data", "pagination"] + "required": [ + "status", + "data", + "pagination" + ] }, "UpdateOrganizationInput": { "type": "object", @@ -22604,7 +24921,14 @@ "type": "string" } }, - "required": ["id", "formId", "formFillerId", "routedToBookingUid", "response", "createdAt"] + "required": [ + "id", + "formId", + "formFillerId", + "routedToBookingUid", + "response", + "createdAt" + ] }, "GetRoutingFormResponsesOutput": { "type": "object", @@ -22612,13 +24936,139 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/RoutingFormResponseOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] + }, + "Routing": { + "type": "object", + "properties": { + "queuedResponseId": { + "type": "string", + "nullable": true, + "description": "The ID of the queued form response. Only present if the form response was queued.", + "example": "123" + }, + "responseId": { + "type": "number", + "nullable": true, + "description": "The ID of the routing form response.", + "example": 123 + }, + "teamMemberIds": { + "description": "Array of team member IDs that were routed to handle this booking.", + "example": [ + 101, + 102 + ], + "type": "array", + "items": { + "type": "number" + } + }, + "teamMemberEmail": { + "type": "string", + "description": "The email of the team member assigned to handle this booking.", + "example": "john.doe@example.com" + }, + "skipContactOwner": { + "type": "boolean", + "description": "Whether to skip contact owner assignment from CRM integration.", + "example": true + }, + "crmAppSlug": { + "type": "string", + "description": "The CRM application slug for integration.", + "example": "salesforce" + }, + "crmOwnerRecordType": { + "type": "string", + "description": "The CRM owner record type for contact assignment.", + "example": "Account" + } + }, + "required": [ + "teamMemberIds" + ] + }, + "CreateRoutingFormResponseOutputData": { + "type": "object", + "properties": { + "eventTypeId": { + "type": "number", + "description": "The ID of the event type that was routed to.", + "example": 123 + }, + "routing": { + "description": "The routing information.", + "example": { + "eventTypeId": 123, + "routing": { + "teamMemberIds": [ + 101, + 102 + ], + "teamMemberEmail": "john.doe@example.com", + "skipContactOwner": true + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/Routing" + } + ] + }, + "routingCustomMessage": { + "type": "string", + "description": "A custom message to be displayed to the user in case of routing to a custom page.", + "example": "This is a custom message." + }, + "routingExternalRedirectUrl": { + "type": "string", + "description": "The external redirect URL to be used in case of routing to a non cal.com event type URL.", + "example": "https://example.com/" + }, + "slots": { + "oneOf": [ + { + "$ref": "#/components/schemas/SlotsOutput_2024_09_04" + }, + { + "$ref": "#/components/schemas/RangeSlotsOutput_2024_09_04" + } + ] + } + } + }, + "CreateRoutingFormResponseOutput": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "success", + "enum": [ + "success", + "error" + ] + }, + "data": { + "$ref": "#/components/schemas/CreateRoutingFormResponseOutputData" + } + }, + "required": [ + "status", + "data" + ] }, "UpdateRoutingFormResponseInput": { "type": "object", @@ -22635,13 +25085,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/RoutingFormResponseOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "RoutingFormOutput": { "type": "object", @@ -22717,7 +25173,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -22726,7 +25185,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "SlotsOutput_2024_09_04": { "type": "object", @@ -22753,7 +25215,10 @@ ] } }, - "required": ["eventTypeId", "slots"] + "required": [ + "eventTypeId", + "slots" + ] }, "ResponseSlotsOutput": { "type": "object", @@ -22761,13 +25226,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ResponseSlotsOutputData" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "ReserveSlotInput_2024_09_04": { "type": "object", @@ -22793,7 +25264,10 @@ "description": "ONLY for authenticated requests with api key, access token or OAuth credentials (ID + secret).\n \n For how many minutes the slot should be reserved - for this long time noone else can book this event type at `start` time. If not provided, defaults to 5 minutes." } }, - "required": ["eventTypeId", "slotStart"] + "required": [ + "eventTypeId", + "slotStart" + ] }, "ReserveSlotOutput_2024_09_04": { "type": "object", @@ -22850,13 +25324,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ReserveSlotOutput_2024_09_04" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetReservedSlotOutput_2024_09_04": { "type": "object", @@ -22864,7 +25344,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "nullable": true, @@ -22875,7 +25358,10 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "MeOrgOutput": { "type": "object", @@ -22887,7 +25373,10 @@ "type": "number" } }, - "required": ["isPlatform", "id"] + "required": [ + "isPlatform", + "id" + ] }, "MeOutput": { "type": "object", @@ -22939,13 +25428,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/MeOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateMeOutput": { "type": "object", @@ -22953,13 +25448,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/MeOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "BookingInputAddressLocation_2024_08_13": { "type": "object", @@ -22970,7 +25471,9 @@ "description": "only allowed value for type is `address` - it refers to address defined by the organizer." } }, - "required": ["type"] + "required": [ + "type" + ] }, "BookingInputAttendeeAddressLocation_2024_08_13": { "type": "object", @@ -22985,7 +25488,10 @@ "example": "123 Example St, City, Country" } }, - "required": ["type", "address"] + "required": [ + "type", + "address" + ] }, "BookingInputAttendeeDefinedLocation_2024_08_13": { "type": "object", @@ -23000,7 +25506,10 @@ "example": "321 Example St, City, Country" } }, - "required": ["type", "location"] + "required": [ + "type", + "location" + ] }, "BookingInputAttendeePhoneLocation_2024_08_13": { "type": "object", @@ -23015,7 +25524,10 @@ "example": "+37120993151" } }, - "required": ["type", "phone"] + "required": [ + "type", + "phone" + ] }, "BookingInputIntegrationLocation_2024_08_13": { "type": "object", @@ -23061,7 +25573,10 @@ ] } }, - "required": ["type", "integration"] + "required": [ + "type", + "integration" + ] }, "BookingInputLinkLocation_2024_08_13": { "type": "object", @@ -23072,7 +25587,9 @@ "description": "only allowed value for type is `link` - it refers to link defined by the organizer." } }, - "required": ["type"] + "required": [ + "type" + ] }, "BookingInputPhoneLocation_2024_08_13": { "type": "object", @@ -23083,7 +25600,9 @@ "description": "only allowed value for type is `phone` - it refers to phone defined by the organizer." } }, - "required": ["type"] + "required": [ + "type" + ] }, "BookingInputOrganizersDefaultAppLocation_2024_08_13": { "type": "object", @@ -23094,7 +25613,9 @@ "description": "only available for team event types and the only allowed value for type is `organizersDefaultApp` - it refers to the default app defined by the organizer." } }, - "required": ["type"] + "required": [ + "type" + ] }, "ValidateBookingLocation_2024_08_13": { "type": "object", @@ -23175,46 +25696,10 @@ "default": "en" } }, - "required": ["name", "timeZone"] - }, - "Routing": { - "type": "object", - "properties": { - "responseId": { - "type": "number", - "description": "The ID of the routing form response that determined this booking assignment.", - "example": 123 - }, - "teamMemberIds": { - "description": "Array of team member IDs that were routed to handle this booking.", - "example": [101, 102], - "type": "array", - "items": { - "type": "number" - } - }, - "teamMemberEmail": { - "type": "string", - "description": "The email of the team member assigned to handle this booking.", - "example": "john.doe@example.com" - }, - "skipContactOwner": { - "type": "boolean", - "description": "Whether to skip contact owner assignment from CRM integration.", - "example": true - }, - "crmAppSlug": { - "type": "string", - "description": "The CRM application slug for integration.", - "example": "salesforce" - }, - "crmOwnerRecordType": { - "type": "string", - "description": "The CRM owner record type for contact assignment.", - "example": "Account" - } - }, - "required": ["responseId", "teamMemberIds"] + "required": [ + "name", + "timeZone" + ] }, "CreateBookingInput_2024_08_13": { "type": "object", @@ -23266,7 +25751,10 @@ }, "guests": { "description": "An optional list of guest emails attending the event.", - "example": ["guest1@example.com", "guest2@example.com"], + "example": [ + "guest1@example.com", + "guest2@example.com" + ], "type": "array", "items": { "type": "string" @@ -23323,7 +25811,10 @@ "description": "Routing information from routing forms that determined the booking assignment. Both responseId and teamMemberIds are required if provided.", "example": { "responseId": 123, - "teamMemberIds": [101, 102] + "teamMemberIds": [ + 101, + 102 + ] }, "allOf": [ { @@ -23332,7 +25823,10 @@ ] } }, - "required": ["start", "attendee"] + "required": [ + "start", + "attendee" + ] }, "CreateInstantBookingInput_2024_08_13": { "type": "object", @@ -23384,7 +25878,10 @@ }, "guests": { "description": "An optional list of guest emails attending the event.", - "example": ["guest1@example.com", "guest2@example.com"], + "example": [ + "guest1@example.com", + "guest2@example.com" + ], "type": "array", "items": { "type": "string" @@ -23441,7 +25938,10 @@ "description": "Routing information from routing forms that determined the booking assignment. Both responseId and teamMemberIds are required if provided.", "example": { "responseId": 123, - "teamMemberIds": [101, 102] + "teamMemberIds": [ + 101, + 102 + ] }, "allOf": [ { @@ -23455,7 +25955,11 @@ "example": true } }, - "required": ["start", "attendee", "instant"] + "required": [ + "start", + "attendee", + "instant" + ] }, "CreateRecurringBookingInput_2024_08_13": { "type": "object", @@ -23507,7 +26011,10 @@ }, "guests": { "description": "An optional list of guest emails attending the event.", - "example": ["guest1@example.com", "guest2@example.com"], + "example": [ + "guest1@example.com", + "guest2@example.com" + ], "type": "array", "items": { "type": "string" @@ -23564,7 +26071,10 @@ "description": "Routing information from routing forms that determined the booking assignment. Both responseId and teamMemberIds are required if provided.", "example": { "responseId": 123, - "teamMemberIds": [101, 102] + "teamMemberIds": [ + 101, + 102 + ] }, "allOf": [ { @@ -23578,7 +26088,10 @@ "example": 5 } }, - "required": ["start", "attendee"] + "required": [ + "start", + "attendee" + ] }, "BookingHost": { "type": "object", @@ -23604,7 +26117,13 @@ "example": "America/Los_Angeles" } }, - "required": ["id", "name", "email", "username", "timeZone"] + "required": [ + "id", + "name", + "email", + "username", + "timeZone" + ] }, "EventType": { "type": "object", @@ -23618,7 +26137,10 @@ "example": "some-event" } }, - "required": ["id", "slug"] + "required": [ + "id", + "slug" + ] }, "BookingAttendee": { "type": "object", @@ -23693,7 +26215,12 @@ "example": "+1234567890" } }, - "required": ["name", "email", "timeZone", "absent"] + "required": [ + "name", + "email", + "timeZone", + "absent" + ] }, "BookingOutput_2024_08_13": { "type": "object", @@ -23722,7 +26249,12 @@ }, "status": { "type": "string", - "enum": ["cancelled", "accepted", "rejected", "pending"], + "enum": [ + "cancelled", + "accepted", + "rejected", + "pending" + ], "example": "accepted" }, "cancellationReason": { @@ -23816,7 +26348,10 @@ } }, "guests": { - "example": ["guest1@example.com", "guest2@example.com"], + "example": [ + "guest1@example.com", + "guest2@example.com" + ], "type": "array", "items": { "type": "string" @@ -23877,7 +26412,12 @@ }, "status": { "type": "string", - "enum": ["cancelled", "accepted", "rejected", "pending"], + "enum": [ + "cancelled", + "accepted", + "rejected", + "pending" + ], "example": "accepted" }, "cancellationReason": { @@ -23971,7 +26511,10 @@ } }, "guests": { - "example": ["guest1@example.com", "guest2@example.com"], + "example": [ + "guest1@example.com", + "guest2@example.com" + ], "type": "array", "items": { "type": "string" @@ -24100,7 +26643,14 @@ } } }, - "required": ["name", "email", "timeZone", "absent", "seatUid", "bookingFieldsResponses"] + "required": [ + "name", + "email", + "timeZone", + "absent", + "seatUid", + "bookingFieldsResponses" + ] }, "CreateSeatedBookingOutput_2024_08_13": { "type": "object", @@ -24129,7 +26679,12 @@ }, "status": { "type": "string", - "enum": ["cancelled", "accepted", "rejected", "pending"], + "enum": [ + "cancelled", + "accepted", + "rejected", + "pending" + ], "example": "accepted" }, "cancellationReason": { @@ -24274,7 +26829,12 @@ }, "status": { "type": "string", - "enum": ["cancelled", "accepted", "rejected", "pending"], + "enum": [ + "cancelled", + "accepted", + "rejected", + "pending" + ], "example": "accepted" }, "cancellationReason": { @@ -24403,7 +26963,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -24429,7 +26992,10 @@ "description": "Booking data, which can be either a BookingOutput object or an array of RecurringBookingOutput objects" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetSeatedBookingOutput_2024_08_13": { "type": "object", @@ -24458,7 +27024,12 @@ }, "status": { "type": "string", - "enum": ["cancelled", "accepted", "rejected", "pending"], + "enum": [ + "cancelled", + "accepted", + "rejected", + "pending" + ], "example": "accepted" }, "cancellationReason": { @@ -24598,7 +27169,12 @@ }, "status": { "type": "string", - "enum": ["cancelled", "accepted", "rejected", "pending"], + "enum": [ + "cancelled", + "accepted", + "rejected", + "pending" + ], "example": "accepted" }, "cancellationReason": { @@ -24722,7 +27298,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -24757,7 +27336,10 @@ "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "RecordingItem": { "type": "object", @@ -24801,7 +27383,14 @@ "example": "Error message" } }, - "required": ["id", "roomName", "startTs", "status", "duration", "shareToken"] + "required": [ + "id", + "roomName", + "startTs", + "status", + "duration", + "shareToken" + ] }, "GetBookingRecordingsOutput": { "type": "object", @@ -24809,7 +27398,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "error": { "type": "object" @@ -24821,7 +27413,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetBookingTranscriptsOutput": { "type": "object", @@ -24829,10 +27424,16 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { - "example": ["https://transcript1.com", "https://transcript2.com"], + "example": [ + "https://transcript1.com", + "https://transcript2.com" + ], "type": "array", "items": { "type": "string" @@ -24842,7 +27443,10 @@ "type": "object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetBookingsOutput_2024_08_13": { "type": "object", @@ -24850,7 +27454,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -24879,7 +27486,11 @@ "type": "object" } }, - "required": ["status", "data", "pagination"] + "required": [ + "status", + "data", + "pagination" + ] }, "RescheduleBookingInput_2024_08_13": { "type": "object", @@ -24899,7 +27510,9 @@ "description": "Reason for rescheduling the booking" } }, - "required": ["start"] + "required": [ + "start" + ] }, "RescheduleSeatedBookingInput_2024_08_13": { "type": "object", @@ -24919,7 +27532,10 @@ "description": "Uid of the specific seat within booking." } }, - "required": ["start", "seatUid"] + "required": [ + "start", + "seatUid" + ] }, "RescheduleBookingOutput_2024_08_13": { "type": "object", @@ -24927,7 +27543,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -24947,7 +27566,10 @@ "description": "Booking data, which can be either a BookingOutput object or a RecurringBookingOutput object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CancelBookingInput_2024_08_13": { "type": "object", @@ -24971,7 +27593,9 @@ "description": "Uid of the specific seat within booking." } }, - "required": ["seatUid"] + "required": [ + "seatUid" + ] }, "CancelBookingOutput_2024_08_13": { "type": "object", @@ -24979,7 +27603,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -25011,7 +27638,10 @@ "description": "Booking data, which can be either a BookingOutput object, a RecurringBookingOutput object, or an array of RecurringBookingOutput objects" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "MarkAbsentAttendee": { "type": "object", @@ -25023,7 +27653,10 @@ "type": "boolean" } }, - "required": ["email", "absent"] + "required": [ + "email", + "absent" + ] }, "MarkAbsentBookingInput_2024_08_13": { "type": "object", @@ -25047,7 +27680,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -25061,7 +27697,10 @@ "description": "Booking data, which can be either a BookingOutput object or a RecurringBookingOutput object" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "ReassignedToDto": { "type": "object", @@ -25079,7 +27718,11 @@ "example": "john.doe@example.com" } }, - "required": ["id", "name", "email"] + "required": [ + "id", + "name", + "email" + ] }, "ReassignBookingOutput_2024_08_13": { "type": "object", @@ -25087,7 +27730,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "oneOf": [ @@ -25103,7 +27749,10 @@ ] } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "ReassignToUserBookingInput_2024_08_13": { "type": "object", @@ -25137,7 +27786,10 @@ "description": "The link to the calendar" } }, - "required": ["label", "link"] + "required": [ + "label", + "link" + ] }, "CalendarLinksOutput_2024_08_13": { "type": "object", @@ -25155,7 +27807,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "BookingReference": { "type": "object", @@ -25178,7 +27833,12 @@ "description": "The id of the booking reference" } }, - "required": ["type", "eventUid", "destinationCalendarId", "id"] + "required": [ + "type", + "eventUid", + "destinationCalendarId", + "id" + ] }, "BookingReferencesOutput_2024_08_13": { "type": "object", @@ -25196,7 +27856,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CreateTeamMembershipInput": { "type": "object", @@ -25211,14 +27874,20 @@ "role": { "type": "string", "default": "MEMBER", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean", "default": false } }, - "required": ["userId"] + "required": [ + "userId" + ] }, "CreateTeamMembershipOutput": { "type": "object", @@ -25226,13 +27895,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetTeamMembershipOutput": { "type": "object", @@ -25240,13 +27915,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "GetTeamMembershipsOutput": { "type": "object", @@ -25254,13 +27935,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UpdateTeamMembershipInput": { "type": "object", @@ -25270,7 +27957,11 @@ }, "role": { "type": "string", - "enum": ["MEMBER", "OWNER", "ADMIN"] + "enum": [ + "MEMBER", + "OWNER", + "ADMIN" + ] }, "disableImpersonation": { "type": "boolean" @@ -25283,13 +27974,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteTeamMembershipOutput": { "type": "object", @@ -25297,13 +27994,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/TeamMembershipOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UserWebhookOutputDto": { "type": "object", @@ -25335,7 +28038,14 @@ "type": "string" } }, - "required": ["payloadTemplate", "userId", "id", "triggers", "subscriberUrl", "active"] + "required": [ + "payloadTemplate", + "userId", + "id", + "triggers", + "subscriberUrl", + "active" + ] }, "UserWebhookOutputResponseDto": { "type": "object", @@ -25343,13 +28053,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/UserWebhookOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UserWebhooksOutputResponseDto": { "type": "object", @@ -25357,7 +28073,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -25366,7 +28085,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "EventTypeWebhookOutputDto": { "type": "object", @@ -25398,7 +28120,14 @@ "type": "string" } }, - "required": ["payloadTemplate", "eventTypeId", "id", "triggers", "subscriberUrl", "active"] + "required": [ + "payloadTemplate", + "eventTypeId", + "id", + "triggers", + "subscriberUrl", + "active" + ] }, "EventTypeWebhookOutputResponseDto": { "type": "object", @@ -25406,13 +28135,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/EventTypeWebhookOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "EventTypeWebhooksOutputResponseDto": { "type": "object", @@ -25420,7 +28155,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -25429,7 +28167,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DeleteManyWebhooksOutputResponseDto": { "type": "object", @@ -25437,13 +28178,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "string" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OAuthClientWebhookOutputDto": { "type": "object", @@ -25475,7 +28222,14 @@ "type": "string" } }, - "required": ["payloadTemplate", "oAuthClientId", "id", "triggers", "subscriberUrl", "active"] + "required": [ + "payloadTemplate", + "oAuthClientId", + "id", + "triggers", + "subscriberUrl", + "active" + ] }, "OAuthClientWebhookOutputResponseDto": { "type": "object", @@ -25483,13 +28237,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/OAuthClientWebhookOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "OAuthClientWebhooksOutputResponseDto": { "type": "object", @@ -25497,7 +28257,10 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "type": "array", @@ -25506,7 +28269,10 @@ } } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "DestinationCalendarsInputBodyDto": { "type": "object", @@ -25515,7 +28281,11 @@ "type": "string", "example": "apple_calendar", "description": "The calendar service you want to integrate, as returned by the /calendars endpoint", - "enum": ["apple_calendar", "google_calendar", "office365_calendar"] + "enum": [ + "apple_calendar", + "google_calendar", + "office365_calendar" + ] }, "externalId": { "type": "string", @@ -25526,7 +28296,10 @@ "type": "string" } }, - "required": ["integration", "externalId"] + "required": [ + "integration", + "externalId" + ] }, "DestinationCalendarsOutputDto": { "type": "object", @@ -25545,7 +28318,12 @@ "nullable": true } }, - "required": ["userId", "integration", "externalId", "credentialId"] + "required": [ + "userId", + "integration", + "externalId", + "credentialId" + ] }, "DestinationCalendarsOutputResponseDto": { "type": "object", @@ -25553,18 +28331,29 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/DestinationCalendarsOutputDto" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "CalendarEventResponseStatus": { "type": "string", "description": "Host's response to the invitation", - "enum": ["accepted", "pending", "declined", "needsAction"] + "enum": [ + "accepted", + "pending", + "declined", + "needsAction" + ] }, "CalendarEventAttendee": { "type": "object", @@ -25593,12 +28382,19 @@ "description": "Indicates if this attendee's attendance is optional" } }, - "required": ["email"] + "required": [ + "email" + ] }, "CalendarEventStatus": { "type": "string", "description": "Status of the event (accepted, pending, declined, cancelled)", - "enum": ["accepted", "pending", "declined", "cancelled"] + "enum": [ + "accepted", + "pending", + "declined", + "cancelled" + ] }, "CalendarEventHost": { "type": "object", @@ -25618,12 +28414,18 @@ "$ref": "#/components/schemas/CalendarEventResponseStatus" } }, - "required": ["email"] + "required": [ + "email" + ] }, "CalendarSource": { "type": "string", "description": "Calendar integration source (e.g., Google Calendar, Office 365, Apple Calendar). Currently only Google Calendar is supported.", - "enum": ["google", "office365", "apple"] + "enum": [ + "google", + "office365", + "apple" + ] }, "UnifiedCalendarEventOutput": { "type": "object", @@ -25717,7 +28519,13 @@ "$ref": "#/components/schemas/CalendarSource" } }, - "required": ["start", "end", "id", "title", "source"] + "required": [ + "start", + "end", + "id", + "title", + "source" + ] }, "GetUnifiedCalendarEventOutput": { "type": "object", @@ -25725,73 +28533,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] - }, - "data": { - "$ref": "#/components/schemas/UnifiedCalendarEventOutput" - } - }, - "required": ["status", "data"] - }, - "CreateRoutingFormResponseOutputData": { - "type": "object", - "properties": { - "eventTypeId": { - "type": "number", - "description": "The ID of the event type that was routed to.", - "example": 123 - }, - "routing": { - "description": "The routing information.", - "example": { - "eventTypeId": 123, - "routing": { - "teamMemberIds": [101, 102], - "teamMemberEmail": "john.doe@example.com", - "skipContactOwner": true - } - }, - "allOf": [ - { - "$ref": "#/components/schemas/Routing" - } - ] - }, - "routingCustomMessage": { - "type": "string", - "description": "A custom message to be displayed to the user in case of routing to a custom page.", - "example": "This is a custom message." - }, - "routingExternalRedirectUrl": { - "type": "string", - "description": "The external redirect URL to be used in case of routing to a non cal.com event type URL.", - "example": "https://example.com/" - }, - "slots": { - "oneOf": [ - { - "$ref": "#/components/schemas/SlotsOutput_2024_09_04" - }, - { - "$ref": "#/components/schemas/RangeSlotsOutput_2024_09_04" - } + "enum": [ + "success", + "error" ] - } - } - }, - "CreateRoutingFormResponseOutput": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "success", - "enum": ["success", "error"] }, "data": { - "$ref": "#/components/schemas/CreateRoutingFormResponseOutputData" + "$ref": "#/components/schemas/UnifiedCalendarEventOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "RequestEmailVerificationInput": { "type": "object", @@ -25802,7 +28556,9 @@ "example": "acme@example.com" } }, - "required": ["email"] + "required": [ + "email" + ] }, "RequestEmailVerificationOutput": { "type": "object", @@ -25810,10 +28566,15 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] } }, - "required": ["status"] + "required": [ + "status" + ] }, "RequestPhoneVerificationInput": { "type": "object", @@ -25824,7 +28585,9 @@ "example": "+372 5555 6666" } }, - "required": ["phone"] + "required": [ + "phone" + ] }, "RequestPhoneVerificationOutput": { "type": "object", @@ -25832,10 +28595,15 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] } }, - "required": ["status"] + "required": [ + "status" + ] }, "VerifyEmailInput": { "type": "object", @@ -25851,7 +28619,10 @@ "example": "1ABG2C" } }, - "required": ["email", "code"] + "required": [ + "email", + "code" + ] }, "WorkingHours": { "type": "object", @@ -25873,7 +28644,11 @@ "nullable": true } }, - "required": ["days", "startTime", "endTime"] + "required": [ + "days", + "startTime", + "endTime" + ] }, "AvailabilityModel": { "type": "object", @@ -25913,7 +28688,12 @@ "nullable": true } }, - "required": ["id", "days", "startTime", "endTime"] + "required": [ + "id", + "days", + "startTime", + "endTime" + ] }, "ScheduleOutput": { "type": "object", @@ -25983,13 +28763,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "VerifyPhoneInput": { "type": "object", @@ -26005,7 +28791,10 @@ "example": "1ABG2C" } }, - "required": ["phone", "code"] + "required": [ + "phone", + "code" + ] }, "UserVerifiedPhoneOutput": { "type": "object", @@ -26013,13 +28802,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UserVerifiedEmailsOutput": { "type": "object", @@ -26027,13 +28822,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "UserVerifiedPhonesOutput": { "type": "object", @@ -26041,13 +28842,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamVerifiedEmailOutput": { "type": "object", @@ -26055,13 +28862,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamVerifiedPhoneOutput": { "type": "object", @@ -26069,13 +28882,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamVerifiedEmailsOutput": { "type": "object", @@ -26083,13 +28902,19 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] }, "TeamVerifiedPhonesOutput": { "type": "object", @@ -26097,14 +28922,20 @@ "status": { "type": "string", "example": "success", - "enum": ["success", "error"] + "enum": [ + "success", + "error" + ] }, "data": { "$ref": "#/components/schemas/ScheduleOutput" } }, - "required": ["status", "data"] + "required": [ + "status", + "data" + ] } } } -} +} \ No newline at end of file