Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/app-store/zapier/DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ items:
- 2.jpg
---

Connect Cal.diy with 8,000+ apps through Zapier to automate your scheduling workflows. Set up triggers for booking events (created, rescheduled, cancelled, meeting ended) and integrate with popular tools like Slack, Gmail, Google Sheets, and more.
Connect Cal.diy with 8,000+ apps through Zapier to automate your scheduling workflows. Set up triggers for booking events (created, rescheduled, cancelled, no-show, meeting ended) and integrate with popular tools like Slack, Gmail, Google Sheets, and more.

**Get started:** Click "Visit" to access the Zapier integrations page and create your first automation workflow.
1 change: 1 addition & 0 deletions packages/app-store/zapier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The following API endpoints are maintained for backward compatibility with exist

- `GET /api/integrations/zapier/listBookings` - List user bookings
- `GET /api/integrations/zapier/listOOOEntries` - List out-of-office entries
- `GET /api/integrations/zapier/listNoShowBookings` - List no-show bookings
- `POST /api/integrations/zapier/addSubscription` - Subscribe to webhooks
- `DELETE /api/integrations/zapier/deleteSubscription` - Unsubscribe from webhooks
- `GET /api/integrations/zapier/me` - Get user/team information
Expand Down
2 changes: 1 addition & 1 deletion packages/app-store/zapier/_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { AppMeta } from "@calcom/types/App";
export const metadata = {
name: "Zapier",
description:
"Workflow automation for everyone. Use the Cal.diy Zapier app to trigger your workflows when a booking is created, rescheduled, or cancelled, or after a meeting ends.",
"Workflow automation for everyone. Use the Cal.diy Zapier app to trigger your workflows when a booking is created, rescheduled, cancelled, marked as no-show, or after a meeting ends.",
installed: true,
category: "automation",
categories: ["automation"],
Expand Down
1 change: 1 addition & 0 deletions packages/app-store/zapier/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { default as add } from "./add";
export { default as listBookings } from "./subscriptions/listBookings";
export { default as listOOOEntries } from "./subscriptions/listOOOEntries";
export { default as listNoShowBookings } from "./subscriptions/listNoShowBookings";
export { default as deleteSubscription } from "./subscriptions/deleteSubscription";
export { default as addSubscription } from "./subscriptions/addSubscription";
export { default as me } from "./subscriptions/me";
100 changes: 100 additions & 0 deletions packages/app-store/zapier/api/subscriptions/listNoShowBookings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import type { NextApiRequest, NextApiResponse } from "next";

import prisma from "@calcom/prisma";
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Filter no-show samples to accepted bookings.

The query currently returns any booking with noShowHost or attendee noShow, including cancelled/rejected/pending bookings if those flags remain set. Add status: BookingStatus.ACCEPTED so this list mirrors actual no-show trigger eligibility.

🛠️ Proposed fix
-import { WebhookTriggerEvents } from "@calcom/prisma/enums";
+import { BookingStatus, WebhookTriggerEvents } from "@calcom/prisma/enums";
@@
   const where = teamId
     ? {
+        status: BookingStatus.ACCEPTED,
         eventType: {
           OR: [{ teamId }, { parent: { teamId } }],
         },
         OR: [{ noShowHost: true }, { attendees: { some: { noShow: true } } }],
       }
     : {
+        status: BookingStatus.ACCEPTED,
         eventType: { userId },
         OR: [{ noShowHost: true }, { attendees: { some: { noShow: true } } }],
       };

Based on learnings, all side effects including no-show triggers are gated on booking.status === ACCEPTED.

Also applies to: 18-28

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app-store/zapier/api/subscriptions/listNoShowBookings.ts` at line 4,
The query that builds the no-show sample list in listNoShowBookings.ts currently
returns bookings where noShowHost or attendee.noShow is set but does not
restrict by booking status; update the query to include status:
BookingStatus.ACCEPTED so only accepted bookings are returned. Locate the query
construction in listNoShowBookings (and related blocks around lines ~18-28) and
add the BookingStatus.ACCEPTED filter (using the BookingStatus enum) alongside
the existing noShowHost / attendee.noShow conditions so the results match the
no-show trigger eligibility.

import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";

import { validateAccountOrApiKey } from "../../lib/validateAccountOrApiKey";

async function handler(req: NextApiRequest, res: NextApiResponse) {
const { account: authorizedAccount, appApiKey: validKey } = await validateAccountOrApiKey(req, [
"READ_BOOKING",
]);
Comment on lines +11 to +13
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Require profile read permission before returning profile fields.

This endpoint authorizes only READ_BOOKING, but returns user and attendee names/emails/time zones. The existing listBookings endpoint requires both READ_BOOKING and READ_PROFILE before returning attendee email, so this should keep the same permission boundary.

🔐 Proposed fix
   const { account: authorizedAccount, appApiKey: validKey } = await validateAccountOrApiKey(req, [
     "READ_BOOKING",
+    "READ_PROFILE",
   ]);

Also applies to: 51-79

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app-store/zapier/api/subscriptions/listNoShowBookings.ts` around
lines 11 - 13, The endpoint currently calls validateAccountOrApiKey and only
requests the "READ_BOOKING" permission but then returns profile fields
(user/attendee names, emails, time zones); update the authorization to require
both "READ_BOOKING" and "READ_PROFILE" (mirror listBookings) by changing the
permissions array passed to validateAccountOrApiKey in listNoShowBookings.ts
(the destructured result assigned to authorizedAccount and validKey) so that
profile fields are only returned when READ_PROFILE is granted, or alternatively
strip profile fields if READ_PROFILE is absent—ensure the check location uses
validateAccountOrApiKey and the behavior matches listBookings.


const userId = validKey ? validKey.userId : authorizedAccount && !authorizedAccount.isTeam ? authorizedAccount.id : null;
const teamId = validKey ? validKey.teamId : authorizedAccount && authorizedAccount.isTeam ? authorizedAccount.id : null;

const where = teamId
? {
eventType: {
OR: [{ teamId }, { parent: { teamId } }],
},
OR: [{ noShowHost: true }, { attendees: { some: { noShow: true } } }],
}
: {
eventType: { userId },
OR: [{ noShowHost: true }, { attendees: { some: { noShow: true } } }],
};

const noShowBookings = await prisma.booking.findMany({
take: 3,
where,
orderBy: {
updatedAt: "desc",
},
select: {
uid: true,
title: true,
description: true,
customInputs: true,
responses: true,
startTime: true,
endTime: true,
createdAt: true,
updatedAt: true,
location: true,
cancellationReason: true,
status: true,
metadata: true,
noShowHost: true,
user: {
select: {
username: true,
name: true,
email: true,
timeZone: true,
locale: true,
},
},
eventType: {
select: {
title: true,
description: true,
requiresConfirmation: true,
price: true,
currency: true,
length: true,
bookingFields: true,
team: true,
},
},
attendees: {
select: {
name: true,
email: true,
timeZone: true,
noShow: true,
},
},
},
});

if (noShowBookings.length === 0) {
return res.status(200).json([]);
}

return res.status(200).json(
noShowBookings.map((booking) => ({
createdAt: booking.updatedAt ?? booking.createdAt,
triggerEvent: WebhookTriggerEvents.BOOKING_NO_SHOW_UPDATED,
payload: {
booking,
},
}))
);
}

export default defaultHandler({
GET: Promise.resolve({ default: defaultResponder(handler) }),
});
2 changes: 1 addition & 1 deletion packages/app-store/zapier/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"categories": ["automation"],
"publisher": "Cal.diy",
"email": "[email protected]",
"description": "Workflow automation for everyone. Use the Cal.diy Zapier app to automate your workflows when a booking is created, rescheduled, cancelled or when a meeting ends.",
"description": "Workflow automation for everyone. Use the Cal.diy Zapier app to automate your workflows when a booking is created, rescheduled, cancelled, marked as no-show, or when a meeting ends.",
"isTemplate": false,
"__createdUsingCli": true,
"__template": "link-as-an-app",
Expand Down
Loading