> ## Documentation Index
> Fetch the complete documentation index at: https://docs.talview.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scheduler

> Developer guide for integrating scheduler with the Proview SDK, including initialization, authentication, UI rendering, events, and full API reference.

## What is Scheduler module

The Scheduler module is part of the Proview SDK, exposed as `Proview.scheduler`. It provides a candidate-facing interface for managing interview scheduler workflows with integrated authentication.

Once initialized, 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 scheduler UI** — mount a prebuilt scheduler 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](#callback-signature) for the callback form.

### 1. Load the SDK and initialize

Declare `Proview.scheduler()` in the `modules` array when calling `Proview.init()`. The `dsn` and `credential` are inherited — no per-module init call needed. The SDK authenticates the user and makes the user object available globally at `Proview.user`.

```ts theme={null}
window.proviewOnLoad = async function () {
  await Proview.init({
    dsn: 'YOUR_PROJECT_DSN',
    credential: Proview.auth.FirebaseAuthStrategy({ token: 'your-firebase-auth-token' }),
    modules: [Proview.scheduler()],
  });

  // Access user object globally
  const user = Proview.user;
  console.log(user.uid, user.email, user.displayName);

  // Get ID token when needed
  const idToken = await user.getIdToken();
};
```

### 2. Fetch meetings

```ts theme={null}
const meetings = await Proview.scheduler.fetchMeetings();
// => MeetingSummary[]
```

### 3. Fetch state for a specific meeting

```ts theme={null}
const state = await Proview.scheduler.fetchMeeting('meeting_123');
// => SchedulerState | null
```

### 4. Respond to an interview

**Accept:**

```ts theme={null}
const updated = await Proview.scheduler.acceptMeeting({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  reason: 'Confirmed',
});
```

**Decline:**

```ts theme={null}
const updated = await Proview.scheduler.declineMeeting({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  reason: 'Not available',
});
```

**Propose a new time** (when the candidate wants to reschedule):

```ts theme={null}
const updated = await Proview.scheduler.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):

```ts theme={null}
const updated = await Proview.scheduler.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 scheduler UI

Mount the scheduler UI into any container element. Three layout modes are supported: `inline`, `sidebar`, and `modal`.

```ts theme={null}
const mounted = Proview.scheduler.mount({
  initialView: 'scheduled_interviews',
  mountMode: {
    mode: 'inline',
    containerId: 'proview-scheduler-root',
  },
});
```

### 6. Navigate programmatically

Use `Proview.scheduler.navigate()` to trigger a view change from outside the UI:

```ts theme={null}
Proview.scheduler.navigate('interviews', { meetingId: 'meeting_123' });
```

Use `mounted.setView()` to change the view from the mounted instance directly:

```ts theme={null}
mounted.setView('scheduled_interviews', { meetingId: 'meeting_123' });
```

### 7. Listen to events

`on()` returns an unsubscribe function, useful for cleanup in component lifecycles:

```ts theme={null}
const unsubscribe = Proview.scheduler.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:

```ts theme={null}
mounted.unmount();
Proview.scheduler.destroy();
```

***

## API Reference

### `Proview.scheduler`

The scheduler module is automatically initialized when `Proview.scheduler()` is included in the `modules` array passed to `Proview.init()`. The `dsn` and `credential` are inherited from the global init — no separate per-module call is needed.

```ts theme={null}
await Proview.init({
  dsn: 'project-dsn',
  credential: Proview.auth.FirebaseAuthStrategy({ token: 'your-token' }),
  modules: [Proview.scheduler()],
});

// Proview.scheduler is now ready
```

`Proview.scheduler` exposes:

```ts theme={null}
type SchedulerSession = {
  fetchMeeting(meetingId: string, cb?: MeetingStateCallback): Promise<SchedulerState | null>;
  fetchMeetings(cb?: MeetingsCallback): Promise<MeetingSummary[]>;
  acceptMeeting(payload: AcceptMeetingInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  declineMeeting(payload: DeclineMeetingInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  proposeNewTime(payload: ProposeNewTimeInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  setAvailability(payload: SetAvailabilityInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  mount(options: MountOptions): MountedSchedulerUi;
  navigate(view: SchedulerView, options: { meetingId: string }): void;
  getCurrentView(): SchedulerView;
  on<E extends SchedulerEventName>(event: E, handler: (payload: SchedulerEventPayloadMap[E]) => void): () => void;
  off<E extends SchedulerEventName>(event: E, handler: (payload: SchedulerEventPayloadMap[E]) => void): void;
  destroy(): void;
};
```

The authenticated user is available globally at `Proview.user` after initialization.

***

## User Object

After initializing the scheduler module, `Proview.user` provides a Firebase-compatible user object accessible globally.

**User Interface**

```ts theme={null}
type User = {
  uid: string;                      // User's unique ID
  email: string | null;             // User's email address
  displayName: string | null;       // User's display name
  emailVerified: boolean;           // Email verification status
  isAnonymous?: boolean;            // Is anonymous user
  metadata?: UserMetadata;          // Creation and last sign-in time
  providerData?: ProviderData[];    // Provider-specific info

  // Methods
  getIdToken(forceRefresh?: boolean): Promise<string>;
  getIdTokenResult(forceRefresh?: boolean): Promise<IdTokenResult>;
};
```

**Example:**

```ts theme={null}
// Access user properties globally after Proview.init()
const user = Proview.user;
console.log(user.uid);
console.log(user.email);
console.log(user.displayName);
console.log(user.emailVerified);

// Get ID token
const idToken = await user.getIdToken();

// Force refresh token
const freshToken = await user.getIdToken(true);
```

***

## Firebase Authentication

### `Proview.auth.FirebaseAuthStrategy(config)`

Creates a Firebase authentication credential for use with the Proview SDK modules.

**Input**

```ts theme={null}
type FirebaseAuthStrategyConfig = {
  token: string;  // Required: Firebase authentication token
};
```

**Returns** — `FirebaseAuthStrategy`

A credential object passed to `Proview.init()`. All modules declared in the `modules` array inherit it automatically.

**Example:**

```ts theme={null}
await Proview.init({
  dsn: 'project-dsn',
  credential: Proview.auth.FirebaseAuthStrategy({
    token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
  }),
  modules: [Proview.scheduler()],
});
```

***

### `fetchMeetings(cb?)`

Returns all meetings for the candidate.

| | |
| - | - |
| **Input** | None |
| **Promise** | `Promise<MeetingSummary[]>` |
| **Callback** | `(error: SchedulerError \| null, meetings?: MeetingSummary[]) => void` |

***

### `fetchMeeting(meetingId, cb?)`

Returns the current scheduler state for one meeting.

| | |
| - | - |
| **Input** | `meetingId: string` |
| **Promise** | `Promise<SchedulerState \| null>` |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `acceptMeeting(payload, cb?)`

Accepts a specific interview within a meeting.

**Input**

```ts theme={null}
type AcceptMeetingInput = {
  meetingId: string;
  interviewId: string;
  reason?: string;
};
```

| | |
| - | - |
| **Promise** | `Promise<SchedulerState>` |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `declineMeeting(payload, cb?)`

Declines a specific interview within a meeting.

**Input**

```ts theme={null}
type DeclineMeetingInput = {
  meetingId: string;
  interviewId: string;
  reason?: string;
};
```

| | |
| - | - |
| **Promise** | `Promise<SchedulerState>` |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `proposeNewTime(payload, cb?)`

Submits a candidate's preferred alternative time slot.

**Input**

```ts theme={null}
type ProposeNewTimeInput = {
  meetingId: string;
  interviewId: string;
  slotId: string;  // ID of the available slot to propose
  note?: string;
};
```

| | |
| - | - |
| **Promise** | `Promise<SchedulerState>` |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `setAvailability(payload, cb?)`

Saves the candidate's general availability for scheduler. Availability is candidate-scoped, not tied to a specific meeting. The `meetingId` provides session context for the operation.

**Input**

```ts theme={null}
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
};
```

| | |
| - | - |
| **Promise** | `Promise<SchedulerState>` |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `mount(options)`

Mounts the scheduler UI into a host element.

**Input**

```ts theme={null}
type SchedulerView = 'scheduled_interviews' | 'interviews';

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

  // 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';
    };
  };
};
```

**Returns** — `MountedSchedulerUi`

```ts theme={null}
type MountedSchedulerUi = {
  unmount(): void;
  setView(
    view: SchedulerView,
    options: {
      meetingId: string;
      // Arbitrary key-value data passed through to 'ui.route.changed' events.
      metadata?: Record<string, string | number | boolean | null>;
    },
  ): void;
};
```

***

### `navigate(view, options)`

Navigates the mounted UI to a target view. Equivalent to `mounted.setView()` but called on the session rather than the mounted instance.

| | |
| - | - |
| **`view`** | `SchedulerView` |
| **`options`** | `{ meetingId: string }` |
| **Returns** | `void` |

***

### `getCurrentView()`

Returns the currently active view name.

| | |
| - | - |
| **Returns** | `SchedulerView` |

***

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

Subscribes or unsubscribes from scheduler events. `on()` returns an unsubscribe function as a convenience alternative to calling `off()`.

**Event names and payloads**

| Event | Payload |
| - | - |
| `state.changed` | `{ previous: SchedulerState; current: SchedulerState; source: 'command' \| 'remote' }` |
| `ui.route.changed` | `{ from: SchedulerView \| null; to: SchedulerView; mode: 'memory' \| 'host_v8'; meetingId?: string }` |
| `ui.rendered` | `{ view: SchedulerView; mode: UiMode; containerId?: string }` |
| `session.error` | `SchedulerError` |
| `session.destroyed` | `{ meetingId: string; participantId: string; destroyedAt: string }` |

**Full type definitions**

```ts theme={null}
type SchedulerPhase =
  | 'loading'
  | 'invited'
  | 'confirmed'
  | 'declined'
  | 'proposed'
  | 'proposal_declined'
  | 'cancelled'
  | 'completed'
  | 'expired';

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

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

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

type SchedulerState = {
  meetingId: string;
  participantId: string;
  phase: SchedulerPhase;
  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: SchedulerView;
  tabs: {
    active: 'upcoming' | 'completed';
  };
  lastSyncedAt?: string; // ISO 8601
};

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

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

***

### `destroy()`

Cleans up the scheduler 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.

```ts theme={null}
type MeetingStateCallback = (error: SchedulerError | null, state?: SchedulerState | null) => void;
type MeetingsCallback = (error: SchedulerError | null, meetings?: MeetingSummary[]) => void;
```

Either style works:

```ts theme={null}
// Promise
const state = await scheduler.acceptMeeting(payload);

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.