/** * Calendly Integration for WORKWAY * * Enables scheduling automation: sync booked meetings to Notion, * alert teams on Slack, create CRM contacts, and compound workflows. * * Key use cases: * - Meeting Scheduled → Notion log + Slack alert + CRM contact * - Meeting Canceled → Update records + notify team * - Available times → Dynamic scheduling links * * @example * ```typescript * import { Calendly } from '@workwayco/integrations/calendly'; * * const calendly = new Calendly({ accessToken: tokens.calendly.access_token }); * * // Get current user * const user = await calendly.getCurrentUser(); * * // List scheduled events * const events = await calendly.listScheduledEvents({ * userUri: user.data.uri, * status: 'active', * }); * * // Verify webhook signature * const event = await calendly.verifyWebhook(payload, signature, signingKey); * ``` */ import { ActionResult } from '@workwayco/sdk'; import { BaseAPIClient } from '../core/index.js'; /** * Calendly integration configuration */ export interface CalendlyConfig { /** OAuth access token */ accessToken: string; /** Optional: Override API endpoint */ apiUrl?: string; /** Request timeout in milliseconds (default: 30000) */ timeout?: number; } /** * Calendly user */ export interface CalendlyUser { uri: string; name: string; slug: string; email: string; scheduling_url: string; timezone: string; avatar_url: string | null; created_at: string; updated_at: string; current_organization: string; resource_type: 'User'; } /** * Organization membership */ export interface CalendlyOrganizationMembership { uri: string; role: 'user' | 'admin' | 'owner'; user: CalendlyUser; organization: string; updated_at: string; created_at: string; } /** * Event type (the template for scheduling) */ export interface CalendlyEventType { uri: string; name: string; active: boolean; booking_method: 'instant' | 'poll'; slug: string; scheduling_url: string; duration: number; kind: 'solo' | 'group'; pooling_type: 'round_robin' | 'collective' | null; type: 'StandardEventType' | 'AdhocEventType'; color: string; created_at: string; updated_at: string; internal_note: string | null; description_plain: string | null; description_html: string | null; profile: { type: 'User' | 'Team'; name: string; owner: string; }; secret: boolean; admin_managed: boolean; custom_questions: CalendlyCustomQuestion[]; } /** * Custom question on event type */ export interface CalendlyCustomQuestion { uuid: string; name: string; type: 'string' | 'text' | 'phone_number' | 'single_select' | 'multi_select'; position: number; enabled: boolean; required: boolean; answer_choices?: string[]; include_other?: boolean; } /** * A scheduled event (a booked meeting) */ export interface CalendlyScheduledEvent { uri: string; name: string; meeting_notes_plain: string | null; meeting_notes_html: string | null; status: 'active' | 'canceled'; start_time: string; end_time: string; event_type: string; location: CalendlyLocation | null; invitees_counter: { total: number; active: number; limit: number; }; created_at: string; updated_at: string; event_memberships: CalendlyEventMembership[]; event_guests: CalendlyEventGuest[]; calendar_event: CalendlyCalendarEvent | null; cancellation?: CalendlyCancellation; } /** * Event location */ export interface CalendlyLocation { type: 'physical' | 'outbound_call' | 'inbound_call' | 'google_conference' | 'zoom' | 'microsoft_teams_conference' | 'webex_conference' | 'gotomeeting_conference' | 'custom'; location?: string; additional_info?: string; join_url?: string; status?: 'initiated' | 'processing' | 'pushed' | 'failed'; data?: Record; } /** * Event membership (host) */ export interface CalendlyEventMembership { user: string; user_email: string; user_name: string; } /** * Event guest */ export interface CalendlyEventGuest { email: string; created_at: string; updated_at: string; } /** * Calendar event sync info */ export interface CalendlyCalendarEvent { kind: 'google' | 'outlook' | 'office365' | 'exchange' | 'icloud'; external_id: string; } /** * Cancellation info */ export interface CalendlyCancellation { canceled_by: string; reason: string | null; canceler_type: 'host' | 'invitee'; created_at: string; } /** * Event invitee (the person who booked) */ export interface CalendlyInvitee { uri: string; email: string; first_name: string | null; last_name: string | null; name: string; status: 'active' | 'canceled'; questions_and_answers: CalendlyQuestionAnswer[]; timezone: string | null; event: string; created_at: string; updated_at: string; tracking: CalendlyTracking | null; text_reminder_number: string | null; rescheduled: boolean; old_invitee: string | null; new_invitee: string | null; cancel_url: string; reschedule_url: string; routing_form_submission: string | null; payment: CalendlyPayment | null; no_show: CalendlyNoShow | null; reconfirmation: CalendlyReconfirmation | null; cancellation?: CalendlyCancellation; } /** * Question and answer from booking */ export interface CalendlyQuestionAnswer { question: string; answer: string; position: number; } /** * UTM tracking parameters */ export interface CalendlyTracking { utm_campaign: string | null; utm_source: string | null; utm_medium: string | null; utm_content: string | null; utm_term: string | null; salesforce_uuid: string | null; } /** * Payment info */ export interface CalendlyPayment { external_id: string; provider: 'stripe' | 'paypal'; amount: number; currency: string; terms: string | null; successful: boolean; } /** * No-show info */ export interface CalendlyNoShow { uri: string; created_at: string; } /** * Reconfirmation info */ export interface CalendlyReconfirmation { created_at: string; confirmed_at: string | null; } /** * Webhook subscription */ export interface CalendlyWebhook { uri: string; callback_url: string; created_at: string; updated_at: string; retry_started_at: string | null; state: 'active' | 'disabled'; events: CalendlyWebhookEventType[]; scope: 'user' | 'organization'; organization: string; user: string | null; creator: string; } /** * Webhook event types */ export type CalendlyWebhookEventType = 'invitee.created' | 'invitee.canceled' | 'invitee_no_show.created' | 'routing_form_submission.created'; /** * Webhook event payload */ export interface CalendlyWebhookEvent { event: CalendlyWebhookEventType; created_at: string; created_by: string; payload: CalendlyWebhookPayload; } /** * Webhook payload (varies by event type) */ export interface CalendlyWebhookPayload { uri?: string; email?: string; name?: string; first_name?: string | null; last_name?: string | null; status?: 'active' | 'canceled'; timezone?: string | null; event?: string; created_at?: string; updated_at?: string; questions_and_answers?: CalendlyQuestionAnswer[]; tracking?: CalendlyTracking | null; cancel_url?: string; reschedule_url?: string; cancellation?: CalendlyCancellation; payment?: CalendlyPayment | null; no_show?: CalendlyNoShow | null; reconfirmation?: CalendlyReconfirmation | null; rescheduled?: boolean; old_invitee?: string | null; new_invitee?: string | null; routing_form_submission?: string | null; scheduled_event?: CalendlyScheduledEvent; submission_time?: string; submitter?: string; submitter_type?: 'ExternalUser'; questions_and_responses?: Array<{ question_uuid: string; question: string; answer: string; }>; routing_form?: string; result?: { type: 'event_type' | 'external_url' | 'custom_message'; value?: string; }; } /** * Available time slot */ export interface CalendlyAvailableTime { status: 'available' | 'unavailable'; invitees_remaining: number; start_time: string; scheduling_url: string; } /** * Options for listing event types */ export interface ListEventTypesOptions { /** Filter by active status */ active?: boolean; /** Organization URI */ organization?: string; /** User URI */ user?: string; /** Sort order */ sort?: 'name:asc' | 'name:desc'; /** Number of results (default: 20, max: 100) */ count?: number; /** Pagination token */ pageToken?: string; } /** * Options for listing scheduled events */ export interface ListScheduledEventsOptions { /** User URI (required if no organization) */ user?: string; /** Organization URI (required if no user) */ organization?: string; /** Filter by invitee email */ inviteeEmail?: string; /** Event status filter */ status?: 'active' | 'canceled'; /** Min start time (ISO 8601) */ minStartTime?: string; /** Max start time (ISO 8601) */ maxStartTime?: string; /** Sort order */ sort?: 'start_time:asc' | 'start_time:desc'; /** Number of results (default: 20, max: 100) */ count?: number; /** Pagination token */ pageToken?: string; } /** * Options for listing invitees */ export interface ListInviteesOptions { /** Scheduled event UUID (required) */ eventUuid: string; /** Filter by status */ status?: 'active' | 'canceled'; /** Sort order */ sort?: 'created_at:asc' | 'created_at:desc'; /** Filter by email */ email?: string; /** Number of results (default: 20, max: 100) */ count?: number; /** Pagination token */ pageToken?: string; } /** * Options for getting available times */ export interface GetAvailableTimesOptions { /** Event type URI (required) */ eventType: string; /** Start of range (ISO 8601) */ startTime: string; /** End of range (ISO 8601) */ endTime: string; } /** * Options for creating a webhook */ export interface CreateWebhookOptions { /** URL to receive webhook events */ url: string; /** Events to subscribe to */ events: CalendlyWebhookEventType[]; /** Organization URI */ organization: string; /** User URI (for user-scoped webhooks) */ user?: string; /** Scope: 'user' or 'organization' */ scope: 'user' | 'organization'; /** Signing key for signature verification */ signingKey?: string; } /** * Paginated response */ export interface CalendlyPaginatedResponse { collection: T[]; pagination: { count: number; next_page: string | null; previous_page: string | null; next_page_token: string | null; previous_page_token: string | null; }; } /** * Calendly Integration * * Weniger, aber besser: Scheduling data and webhooks for compound workflows. */ export declare class Calendly extends BaseAPIClient { constructor(config: CalendlyConfig); /** * Get the current authenticated user */ getCurrentUser(): Promise>; /** * Get a user by URI */ getUser(userUri: string): Promise>; /** * List event types */ listEventTypes(options?: ListEventTypesOptions): Promise>>; /** * Get an event type by URI */ getEventType(eventTypeUri: string): Promise>; /** * List scheduled events (booked meetings) */ listScheduledEvents(options?: ListScheduledEventsOptions): Promise>>; /** * Get a scheduled event by URI */ getScheduledEvent(eventUri: string): Promise>; /** * Cancel a scheduled event */ cancelScheduledEvent(eventUri: string, reason?: string): Promise>; /** * List invitees for a scheduled event */ listInvitees(options: ListInviteesOptions): Promise>>; /** * Get an invitee by URI */ getInvitee(inviteeUri: string): Promise>; /** * Mark an invitee as a no-show */ markNoShow(inviteeUri: string): Promise>; /** * Remove a no-show marking */ removeNoShow(noShowUri: string): Promise>; /** * Get available times for an event type */ getAvailableTimes(options: GetAvailableTimesOptions): Promise>; /** * List webhook subscriptions */ listWebhooks(options: { organization: string; user?: string; scope: 'user' | 'organization'; }): Promise>>; /** * Get a webhook by URI */ getWebhook(webhookUri: string): Promise>; /** * Create a webhook subscription */ createWebhook(options: CreateWebhookOptions): Promise>; /** * Delete a webhook subscription */ deleteWebhook(webhookUri: string): Promise>; /** * Verify and parse a webhook event * * Calendly signs webhooks with HMAC SHA-256. * Header: Calendly-Webhook-Signature * Format: t={timestamp},v1={signature} * * @param payload - Raw request body as string * @param signature - Value of Calendly-Webhook-Signature header * @param signingKey - Webhook signing key from webhook creation */ verifyWebhook(payload: string, signature: string, signingKey: string): Promise>; /** * Parse webhook event without verification * Use only for development/testing */ parseWebhookUnsafe(payload: string): ActionResult; /** * Extract meeting details from a webhook event for easy consumption * * Zuhandenheit: Developer thinks "get meeting details" * not "navigate nested webhook payload structure" */ static extractMeetingDetails(event: CalendlyWebhookEvent): { eventType: CalendlyWebhookEventType; inviteeName: string; inviteeEmail: string; eventName: string | null; startTime: string | null; endTime: string | null; location: string | null; joinUrl: string | null; cancelUrl: string | null; rescheduleUrl: string | null; questionsAndAnswers: Record; timezone: string | null; isCanceled: boolean; cancellationReason: string | null; }; /** * Format meeting time for display */ static formatMeetingTime(startTime: string, endTime: string, timezone?: string): string; /** * Extract UUID from Calendly URI * URIs are formatted as: https://api.calendly.com/{resource}/{uuid} */ private extractUuid; /** * Compute HMAC SHA-256 signature in hex (Calendly's format) */ private computeHmacSha256Hex; /** * Get capabilities for Calendly actions */ private getCapabilities; } //# sourceMappingURL=index.d.ts.map