Scheduling Events

Scheduling Events

Scheduling events notify you about calendar and meeting-related activities.

Available Events

EventDescription
CALENDAR_MEETING_SCHEDULEDA meeting has been scheduled
CALENDAR_MEETING_UPDATEDA meeting was updated (decline, accept, or detail change)
CALENDAR_MEETING_CANCELLEDA meeting has been cancelled
CALENDAR_MEETING_RESCHEDULEDA meeting has been rescheduled
CALENDAR_AVAILABILITY_NOT_FOUNDNo available time slots found
CALENDAR_MORE_TIME_REQUESTEDCandidate requested more availability options
CALENDAR_NOTE_TAKER_COMPLETEDMeeting 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

FieldTypeDescription
titlestringMeeting title
startDateTimestringISO 8601 start time
endDateTimestringISO 8601 end time
timezonestringIANA timezone (e.g., America/New_York)
locationstringPhysical meeting location (only for in-person meetings)
videoConferencebooleanWhether this is a video conference
meetingUrlstringVideo conference URL (present when videoConference is true)
participantsarrayList 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

FieldTypeDescription
conversationIdstringPopp conversation ID
externalConversationIdstringYour external conversation reference
externalCampaignIdstringYour external campaign reference
meetingDetailsobjectMeeting 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

FieldTypeDescription
conversationIdstringPopp conversation ID
externalConversationIdstringYour external conversation reference
externalCampaignIdstringYour external campaign reference
updateTypestringType of update (see below)
participantRolestringCANDIDATE or INTERVIEWER
updatedBystringEmail of the participant who triggered the update. null for DETAILS_CHANGED
changedFieldsarrayList of changed field names. Only populated for DETAILS_CHANGED
meetingDetailsobjectCurrent meeting state after the update

Update Types

ValueDescription
PARTICIPANT_DECLINEDA participant declined the meeting invitation
PARTICIPANT_ACCEPTEDA participant accepted the meeting invitation
DETAILS_CHANGEDMeeting 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 booked
  • INTERVIEWER -- the interviewer or hiring manager. Always INTERVIEWER for DETAILS_CHANGED since 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

FieldTypeDescription
noteTakerTranscriptSummarystringAI-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:

  1. If no participant on the meeting template has autoCollectAvailability enabled, the conversation is marked as Needs Review in the Popp dashboard and a CONVERSATION_NEEDS_REVIEW event is also emitted
  2. 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

FieldTypeDescription
recordIdstringPopp conversation ID
organizationIdstringYour Popp organization ID
externalIdstringYour external conversation reference (if provided at creation)
externalCampaignIdstringYour external campaign reference (if provided at creation)
campaignIdstringPopp campaign ID
calendarMeetingTemplateIdstringThe meeting template ID associated with this conversation
suggestedTimesarrayOptional. Up to 5 { start, end } slots the candidate suggested, ISO 8601 UTC. Omitted when the candidate did not suggest times
candidateTimezonestringOptional. The candidate's IANA timezone (e.g. Europe/London). Omitted when not provided
candidateNotesstringOptional. Free-text note from the candidate, max 500 characters. Omitted when not provided

Recommended Actions

When you receive this event, you should:

  1. Review the request - Check if additional availability can be provided
  2. Check suggestedTimes - When present, the candidate has told you exactly when they are free; prioritise interviewer slots that overlap with them
  3. Gather new availability - Collect updated time slots from the interviewers
  4. 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