Scheduling Events
Scheduling Events
Scheduling events notify you about calendar and meeting-related activities.
Available Events
| Event | Description |
|---|---|
CALENDAR_MEETING_SCHEDULED | A meeting has been scheduled |
CALENDAR_MEETING_UPDATED | A meeting was updated (decline, accept, or detail change) |
CALENDAR_MEETING_CANCELLED | A meeting has been cancelled |
CALENDAR_MEETING_RESCHEDULED | A meeting has been rescheduled |
CALENDAR_AVAILABILITY_NOT_FOUND | No available time slots found |
CALENDAR_MORE_TIME_REQUESTED | Candidate requested more availability options |
CALENDAR_NOTE_TAKER_COMPLETED | Meeting notes are ready |
Meeting Details Object
All scheduling events include a meetingDetails object with comprehensive meeting information:
{
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-15T14:00:00.000Z",
"endDateTime": "2024-01-15T14:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
},
{
"email": "[email protected]",
"name": "Jane Smith"
}
]
}
}Meeting Details Fields
| Field | Type | Description |
|---|---|---|
title | string | Meeting title |
startDateTime | string | ISO 8601 start time |
endDateTime | string | ISO 8601 end time |
timezone | string | IANA timezone (e.g., America/New_York) |
location | string | Physical meeting location (only for in-person meetings) |
videoConference | boolean | Whether this is a video conference |
meetingUrl | string | Video conference URL (present when videoConference is true) |
participants | array | List of meeting participants |
CALENDAR_MEETING_SCHEDULED
Triggered when a candidate successfully schedules a meeting.
Payload
{
"event": "CALENDAR_MEETING_SCHEDULED",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T10:30:00.000Z",
"data": {
"recordId": "booking_xyz789",
"organizationId": "org_123456",
"conversationId": "conv_abc123",
"externalConversationId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-15T14:00:00.000Z",
"endDateTime": "2024-01-15T14:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
},
{
"email": "[email protected]",
"name": "Jane Smith"
}
]
}
}
}Data Fields
| Field | Type | Description |
|---|---|---|
conversationId | string | Popp conversation ID |
externalConversationId | string | Your external conversation reference |
externalCampaignId | string | Your external campaign reference |
meetingDetails | object | Meeting details |
CALENDAR_MEETING_UPDATED
Triggered when a booked meeting is updated. This includes participant RSVP changes (decline or accept) and meeting detail changes (time, title, location, participants). Only fires for active bookings -- cancelled or rescheduled bookings are excluded.
Payload
{
"event": "CALENDAR_MEETING_UPDATED",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T10:30:00.000Z",
"data": {
"recordId": "booking_xyz789",
"organizationId": "org_123456",
"conversationId": "conv_abc123",
"externalConversationId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"updateType": "PARTICIPANT_DECLINED",
"participantRole": "CANDIDATE",
"updatedBy": "[email protected]",
"changedFields": [],
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-15T14:00:00.000Z",
"endDateTime": "2024-01-15T14:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
},
{
"email": "[email protected]",
"name": "Jane Smith"
}
]
}
}
}Data Fields
| Field | Type | Description |
|---|---|---|
conversationId | string | Popp conversation ID |
externalConversationId | string | Your external conversation reference |
externalCampaignId | string | Your external campaign reference |
updateType | string | Type of update (see below) |
participantRole | string | CANDIDATE or INTERVIEWER |
updatedBy | string | Email of the participant who triggered the update. null for DETAILS_CHANGED |
changedFields | array | List of changed field names. Only populated for DETAILS_CHANGED |
meetingDetails | object | Current meeting state after the update |
Update Types
| Value | Description |
|---|---|
PARTICIPANT_DECLINED | A participant declined the meeting invitation |
PARTICIPANT_ACCEPTED | A participant accepted the meeting invitation |
DETAILS_CHANGED | Meeting details changed (time, title, location, participants) |
Participant Role
The participantRole field identifies whether the person who triggered the update is the candidate or the interviewer:
CANDIDATE-- the candidate whose meeting was bookedINTERVIEWER-- the interviewer or hiring manager. AlwaysINTERVIEWERforDETAILS_CHANGEDsince only the organizer side can modify meeting details
Changed Fields
When updateType is DETAILS_CHANGED, the changedFields array lists which fields were modified. Possible values: startDateTime, endDateTime, title, location, participants.
For PARTICIPANT_DECLINED and PARTICIPANT_ACCEPTED, changedFields is an empty array.
Example: DETAILS_CHANGED
{
"event": "CALENDAR_MEETING_UPDATED",
"eventId": "evt_def456",
"eventTimestamp": "2024-01-16T09:00:00.000Z",
"data": {
"recordId": "booking_xyz789",
"organizationId": "org_123456",
"conversationId": "conv_abc123",
"externalConversationId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"updateType": "DETAILS_CHANGED",
"participantRole": "INTERVIEWER",
"updatedBy": null,
"changedFields": ["startDateTime", "endDateTime"],
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-17T15:00:00.000Z",
"endDateTime": "2024-01-17T15:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
},
{
"email": "[email protected]",
"name": "Jane Smith"
}
]
}
}
}Note: updatedBy is null for DETAILS_CHANGED because the calendar provider does not identify which user changed the meeting details. participantRole is always INTERVIEWER since only the organizer side can modify event details.
CALENDAR_MEETING_CANCELLED
Triggered when a scheduled meeting is cancelled.
Payload
{
"event": "CALENDAR_MEETING_CANCELLED",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T10:30:00.000Z",
"data": {
"recordId": "booking_xyz789",
"organizationId": "org_123456",
"conversationId": "conv_abc123",
"externalConversationId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-15T14:00:00.000Z",
"endDateTime": "2024-01-15T14:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
}
]
}
}
}CALENDAR_MEETING_RESCHEDULED
Triggered when a meeting is moved to a different time.
Payload
{
"event": "CALENDAR_MEETING_RESCHEDULED",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T10:30:00.000Z",
"data": {
"recordId": "booking_xyz789",
"organizationId": "org_123456",
"conversationId": "conv_abc123",
"externalConversationId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-16T15:00:00.000Z",
"endDateTime": "2024-01-16T15:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
}
]
}
}
}CALENDAR_NOTE_TAKER_COMPLETED
Triggered when the meeting note-taker has finished processing a meeting recording.
Payload
{
"event": "CALENDAR_NOTE_TAKER_COMPLETED",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T16:00:00.000Z",
"data": {
"recordId": "booking_xyz789",
"organizationId": "org_123456",
"conversationId": "conv_abc123",
"externalConversationId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"noteTakerTranscriptSummary": "Summary of the interview: The candidate demonstrated strong technical skills...",
"meetingDetails": {
"title": "Interview with John Doe",
"startDateTime": "2024-01-15T14:00:00.000Z",
"endDateTime": "2024-01-15T14:30:00.000Z",
"timezone": "America/New_York",
"videoConference": true,
"meetingUrl": "https://meet.google.com/abc-defg-hij",
"participants": [
{
"email": "[email protected]",
"name": "John Doe"
}
]
}
}
}Additional Data Fields
| Field | Type | Description |
|---|---|---|
noteTakerTranscriptSummary | string | AI-generated summary of the meeting |
CALENDAR_AVAILABILITY_NOT_FOUND
Triggered when using non-connected calendars with autoCollectAvailability set to false and pre-defined availability passed via the API, but the system could not find matching time slots to offer the candidate.
This typically occurs when creating an auto-schedule conversation via the Create Auto-Schedule Conversation API endpoint and the provided availability time slots don't produce valid meeting options.
Payload
{
"event": "CALENDAR_AVAILABILITY_NOT_FOUND",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T10:30:00.000Z",
"data": {
"recordId": "conv_xyz789",
"organizationId": "org_123456"
}
}Common Reasons
- Provided time slots have already passed
- No overlapping availability between participants
- Time slots don't meet minimum meeting duration requirements
CALENDAR_MORE_TIME_REQUESTED
Triggered when a candidate clicks "Request additional time" on the booking page, indicating that none of the available time slots work for them. This event fires for every meeting template configuration: both when Popp collects fresh availability automatically (autoCollectAvailability enabled for at least one participant) and when it does not.
The candidate can optionally suggest up to 5 time slots they are available, along with their timezone and a free-text note. When provided, these are included in the payload so you can schedule against the candidate's actual availability instead of asking them again.
When this event is triggered:
- If no participant on the meeting template has
autoCollectAvailabilityenabled, the conversation is marked as Needs Review in the Popp dashboard and aCONVERSATION_NEEDS_REVIEWevent is also emitted - You can use the Resend Booking URL API endpoint to send new availability options
Payload
{
"event": "CALENDAR_MORE_TIME_REQUESTED",
"eventId": "evt_abc123",
"eventTimestamp": "2024-01-15T10:30:00.000Z",
"data": {
"recordId": "conv_abc123",
"organizationId": "org_123456",
"externalId": "your_conversation_id",
"externalCampaignId": "your_campaign_id",
"campaignId": "campaign_xyz789",
"calendarMeetingTemplateId": "template_abc123",
"suggestedTimes": [
{ "start": "2024-01-20T09:00:00.000Z", "end": "2024-01-20T10:00:00.000Z" },
{ "start": "2024-01-21T14:00:00.000Z", "end": "2024-01-21T15:00:00.000Z" }
],
"candidateTimezone": "Europe/London",
"candidateNotes": "Mornings work best for me"
}
}Data Fields
| Field | Type | Description |
|---|---|---|
recordId | string | Popp conversation ID |
organizationId | string | Your Popp organization ID |
externalId | string | Your external conversation reference (if provided at creation) |
externalCampaignId | string | Your external campaign reference (if provided at creation) |
campaignId | string | Popp campaign ID |
calendarMeetingTemplateId | string | The meeting template ID associated with this conversation |
suggestedTimes | array | Optional. Up to 5 { start, end } slots the candidate suggested, ISO 8601 UTC. Omitted when the candidate did not suggest times |
candidateTimezone | string | Optional. The candidate's IANA timezone (e.g. Europe/London). Omitted when not provided |
candidateNotes | string | Optional. Free-text note from the candidate, max 500 characters. Omitted when not provided |
Recommended Actions
When you receive this event, you should:
- Review the request - Check if additional availability can be provided
- Check
suggestedTimes- When present, the candidate has told you exactly when they are free; prioritise interviewer slots that overlap with them - Gather new availability - Collect updated time slots from the interviewers
- Resend booking URL - Use the Resend Booking URL API to send a new booking link with updated availability
Example Response
case 'CALENDAR_MORE_TIME_REQUESTED':
// Optionally, automatically resend with extended availability
await resendBookingUrl({
conversationId: data.conversationId,
meetingConfiguration: extendedAvailabilityConfig,
meetingParticipants: updatedParticipants,
});
break;Example: Handling Scheduling Events
interface MeetingDetails {
title: string;
startDateTime: string;
endDateTime: string;
timezone: string;
location?: string;
videoConference: boolean;
meetingUrl?: string;
participants: { email: string; name: string }[];
}
interface SchedulingWebhookData {
recordId: string;
organizationId: string;
conversationId?: string;
externalConversationId?: string;
externalCampaignId?: string;
campaignId?: string;
calendarMeetingTemplateId?: string;
noteTakerTranscriptSummary?: string;
meetingDetails?: MeetingDetails;
}
function handleSchedulingWebhook(payload: {
event: string;
eventId: string;
eventTimestamp: string;
data: SchedulingWebhookData;
}) {
const { event, data } = payload;
switch (event) {
case 'CALENDAR_MEETING_SCHEDULED':
// Update your calendar with the new meeting
syncMeeting({
conversationId: data.conversationId,
startTime: data.meetingDetails?.startDateTime,
endTime: data.meetingDetails?.endDateTime,
meetingUrl: data.meetingDetails?.meetingUrl,
});
break;
case 'CALENDAR_MEETING_CANCELLED':
// Remove from calendar, notify relevant parties
removeMeetingFromCalendar(data.conversationId);
break;
case 'CALENDAR_MEETING_RESCHEDULED':
// Update calendar with new time
updateMeetingTime({
conversationId: data.conversationId,
newStartTime: data.meetingDetails?.startDateTime,
newEndTime: data.meetingDetails?.endDateTime,
});
break;
case 'CALENDAR_NOTE_TAKER_COMPLETED':
// Save meeting notes to your CRM
saveMeetingNotes({
conversationId: data.conversationId,
summary: data.noteTakerTranscriptSummary,
});
break;
case 'CALENDAR_AVAILABILITY_NOT_FOUND':
// Alert team to manually find a time
alertSchedulingTeam(data.recordId);
break;
case 'CALENDAR_MORE_TIME_REQUESTED':
// Candidate needs different availability options
notifyHiringManager({
conversationId: data.conversationId,
message: 'Candidate requested more time options',
});
// Optionally resend booking URL with new availability
break;
}
}Use Cases
- Calendar Sync: Keep external calendars synchronized with Popp meetings
- Notifications: Send Slack/email alerts when meetings are scheduled or cancelled
- Analytics: Track scheduling success rates and candidate engagement
- Meeting Notes: Automatically store AI-generated meeting summaries in your CRM
- Availability Management: Respond to candidates who need different time slots via the Resend Booking URL API