Skip to main content

What is Scheduling module

The Scheduling module is part of the Talview SDK, exposed as Proview.scheduling. It provides a candidate-facing interface for managing interview scheduling workflows. Once initialized with a token, it lets your application:
  • Fetch meetings — retrieve a candidate’s scheduled interviews and their current state.
  • Respond to interviews — accept, decline, propose a new time, or submit availability on behalf of the candidate.
  • Render scheduling UI — mount a prebuilt scheduling interface in inline, sidebar, or modal layout.
  • React to state changes — subscribe to events for phase transitions, route changes, and errors.
Both Promise-based and callback-based async patterns are supported. All UI text is localizable via the locale option.

Quickstart

All async methods support both Promises and an optional callback. This walkthrough uses Promises. See Callback Signature for the callback form.

1. Initialize

Call Proview.scheduling.init() once with a token provider. The SDK resolves the candidate identity from the token.
const scheduling = Proview.scheduling.init({
  getToken: async () => {
    // Return a valid access token for the current candidate.
    return accessToken;
  },
});

2. Fetch meetings

const meetings = await scheduling.fetchMeetings();
// => MeetingSummary[]

3. Fetch state for a specific meeting

const state = await scheduling.fetchMeeting('meeting_123');
// => SchedulingState | null

4. Respond to an interview

Accept:
const updated = await scheduling.acceptMeeting({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  reason: 'Confirmed',
});
Decline:
const updated = await scheduling.declineMeeting({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  reason: 'Not available',
});
Propose a new time (when the candidate wants to reschedule):
const updated = await scheduling.proposeNewTime({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  slotId: 'slot_789',
  note: 'Prefer morning slots',
});
Set availability (saves the candidate’s general availability; not scoped to a single meeting):
const updated = await scheduling.setAvailability({
  meetingId: 'meeting_123',
  weekly: {
    monday: [{ start: '09:00', end: '17:00' }],
    wednesday: [{ start: '10:00', end: '15:00' }],
  },
  blockedDates: ['2026-04-01'],
});

5. Render the scheduling UI

Mount the scheduling UI into any container element. Three layout modes are supported: inline, sidebar, and modal.
const mounted = scheduling.mount({
  initialView: 'scheduled_interviews',
  mountMode: {
    mode: 'inline',
    containerId: 'proview-scheduling-root',
  },
});

6. Navigate programmatically

Use scheduling.navigate() to trigger a view change from outside the UI:
scheduling.navigate('interviews', { meetingId: 'meeting_123' });
Use mounted.setView() to change the view from the mounted instance directly:
mounted.setView('scheduled_interviews', { meetingId: 'meeting_123' });

7. Listen to events

on() returns an unsubscribe function, useful for cleanup in component lifecycles:
const unsubscribe = scheduling.on('state.changed', ({ previous, current }) => {
  console.log('Phase changed:', previous.phase, '->', current.phase);
});

// Later, to stop listening:
unsubscribe();

8. Teardown

When done, unmount the UI and destroy the session:
mounted.unmount();
scheduling.destroy();

API Reference

Proview.scheduling.init(input)

Creates a scheduling session. Input
type SchedulingInitInput = {
  getToken: () => Promise<string>;
};
ReturnsSchedulingSession
type SchedulingSession = {
  fetchMeeting(meetingId: string, cb?: MeetingStateCallback): Promise<SchedulingState | null>;
  fetchMeetings(cb?: MeetingsCallback): Promise<MeetingSummary[]>;
  acceptMeeting(payload: AcceptMeetingInput, cb?: MeetingStateCallback): Promise<SchedulingState>;
  declineMeeting(payload: DeclineMeetingInput, cb?: MeetingStateCallback): Promise<SchedulingState>;
  proposeNewTime(payload: ProposeNewTimeInput, cb?: MeetingStateCallback): Promise<SchedulingState>;
  setAvailability(payload: SetAvailabilityInput, cb?: MeetingStateCallback): Promise<SchedulingState>;
  mount(options: MountOptions): MountedSchedulingUi;
  navigate(view: SchedulingView, options: { meetingId: string }): void;
  getCurrentView(): SchedulingView;
  on<E extends SchedulingEventName>(event: E, handler: (payload: SchedulingEventPayloadMap[E]) => void): () => void;
  off<E extends SchedulingEventName>(event: E, handler: (payload: SchedulingEventPayloadMap[E]) => void): void;
  destroy(): void;
};

fetchMeetings(cb?)

Returns all meetings for the candidate.
InputNone
PromisePromise<MeetingSummary[]>
Callback(error: SchedulingError | null, meetings?: MeetingSummary[]) => void

fetchMeeting(meetingId, cb?)

Returns the current scheduling state for one meeting.
InputmeetingId: string
PromisePromise<SchedulingState | null>
Callback(error: SchedulingError | null, state?: SchedulingState | null) => void

acceptMeeting(payload, cb?)

Accepts a specific interview within a meeting. Input
type AcceptMeetingInput = {
  meetingId: string;
  interviewId: string;
  reason?: string;
};
PromisePromise<SchedulingState>
Callback(error: SchedulingError | null, state?: SchedulingState | null) => void

declineMeeting(payload, cb?)

Declines a specific interview within a meeting. Input
type DeclineMeetingInput = {
  meetingId: string;
  interviewId: string;
  reason?: string;
};
PromisePromise<SchedulingState>
Callback(error: SchedulingError | null, state?: SchedulingState | null) => void

proposeNewTime(payload, cb?)

Submits a candidate’s preferred alternative time slot. Input
type ProposeNewTimeInput = {
  meetingId: string;
  interviewId: string;
  slotId: string;  // ID of the available slot to propose
  note?: string;
};
PromisePromise<SchedulingState>
Callback(error: SchedulingError | null, state?: SchedulingState | null) => void

setAvailability(payload, cb?)

Saves the candidate’s general availability for scheduling. Availability is candidate-scoped, not tied to a specific meeting. The meetingId provides session context for the operation. Input
type SetAvailabilityInput = {
  meetingId: string;
  // Recurring weekly time windows. start/end use HH:MM (24-hour) format.
  weekly: Partial<
    Record<
      'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday',
      Array<{ start: string; end: string }>
    >
  >;
  blockedDates: string[]; // Specific dates to exclude, in YYYY-MM-DD format
};
PromisePromise<SchedulingState>
Callback(error: SchedulingError | null, state?: SchedulingState | null) => void

mount(options)

Mounts the scheduling UI into a host element. Input
type SchedulingView = 'scheduled_interviews' | 'interviews';

type MountOptions = {
  // View to show on first render. Defaults to 'scheduled_interviews'.
  initialView?: SchedulingView;

  // BCP 47 locale tag (e.g. 'en-US', 'fr-FR'). Defaults to the browser locale.
  locale?: string;

  // 'sdk' — the SDK renders its own error UI.
  // 'host' — errors are surfaced via the 'session.error' event for the host app to handle.
  errorMode?: 'sdk' | 'host';

  mountMode:
    | { mode: 'inline'; containerId: string }
    | { mode: 'sidebar'; containerId: string; width?: number }
    | { mode: 'modal'; containerId: string; closeOnBackdrop?: boolean; title?: string };

  views?: {
    interviews?: {
      // Which tab to show first in the interviews view.
      initialTab?: 'upcoming' | 'completed';
    };
  };
};
ReturnsMountedSchedulingUi
type MountedSchedulingUi = {
  unmount(): void;
  setView(
    view: SchedulingView,
    options: {
      meetingId: string;
      // Arbitrary key-value data passed through to 'ui.route.changed' events.
      metadata?: Record<string, string | number | boolean | null>;
    },
  ): void;
};

Navigates the mounted UI to a target view. Equivalent to mounted.setView() but called on the session rather than the mounted instance.
viewSchedulingView
options{ meetingId: string }
Returnsvoid

getCurrentView()

Returns the currently active view name.
ReturnsSchedulingView

on(event, handler) / off(event, handler)

Subscribes or unsubscribes from scheduling events. on() returns an unsubscribe function as a convenience alternative to calling off(). Event names and payloads
EventPayload
state.changed{ previous: SchedulingState; current: SchedulingState; source: 'command' | 'remote' }
ui.route.changed{ from: SchedulingView | null; to: SchedulingView; mode: 'memory' | 'host_v8'; meetingId?: string }
ui.rendered{ view: SchedulingView; mode: UiMode; containerId?: string }
session.errorSchedulingError
session.destroyed{ meetingId: string; participantId: string; destroyedAt: string }
Full type definitions
type SchedulingPhase =
  | 'loading'
  | 'invited'
  | 'confirmed'
  | 'declined'
  | 'proposed'
  | 'proposal_declined'
  | 'cancelled'
  | 'completed'
  | 'expired';

type UiMode = 'inline' | 'sidebar' | 'modal';

type SchedulingError = {
  code: string;
  message: string;
  recoverable: boolean;
  source: 'network' | 'auth' | 'validation' | 'internal';
};

type MeetingSummary = {
  meetingId: string;
  phase: SchedulingPhase;
  startAt: string; // ISO 8601
  endAt: string;   // ISO 8601
  title: string;
  round: string;
  mode: string;
};

type SchedulingState = {
  meetingId: string;
  participantId: string;
  phase: SchedulingPhase;
  interviews: Array<{
    interviewId: string;
    title: string;
    round: string;
    type: string;
    mode: string;
    jobId: string;
    startAt: string; // ISO 8601
    endAt: string;   // ISO 8601
    rsvpStatus: 'pending' | 'confirmed' | 'declined' | 'proposed';
    join: { enabled: boolean; url?: string };
  }>;
  availability: {
    weekly: Record<string, Array<{ start: string; end: string }>>;
    blockedDates: string[]; // YYYY-MM-DD
  };
  view: SchedulingView;
  tabs: {
    active: 'upcoming' | 'completed';
  };
  lastSyncedAt?: string; // ISO 8601
};

type SchedulingEventName =
  | 'state.changed'
  | 'ui.route.changed'
  | 'ui.rendered'
  | 'session.error'
  | 'session.destroyed';

type SchedulingEventPayloadMap = {
  'state.changed': {
    previous: SchedulingState;
    current: SchedulingState;
    source: 'command' | 'remote';
  };
  'ui.route.changed': {
    from: SchedulingView | null;
    to: SchedulingView;
    mode: 'memory' | 'host_v8';
    meetingId?: string;
  };
  'ui.rendered': {
    view: SchedulingView;
    mode: UiMode;
    containerId?: string;
  };
  'session.error': SchedulingError;
  'session.destroyed': {
    meetingId: string;
    participantId: string;
    destroyedAt: string;
  };
};

destroy()

Cleans up the scheduling session and all event listeners. Call mounted.unmount() first if the UI is still mounted.

Callback Signature

All async methods accept an optional callback as the last argument. The callback uses a Node.js-style error-first signature.
type MeetingStateCallback = (error: SchedulingError | null, state?: SchedulingState | null) => void;
type MeetingsCallback = (error: SchedulingError | null, meetings?: MeetingSummary[]) => void;
Either style works:
// Promise
const state = await scheduling.acceptMeeting(payload);

// Callback
scheduling.acceptMeeting(payload, (error, state) => {
  if (error) return;
  // handle updated state
});