/** * Typeform Integration for WORKWAY * * Enables form response syncing to Notion, Slack notifications on submissions, * and compound workflows that go beyond Typeform's native one-form-per-workspace limit. * * Key differentiators over native Typeform → Notion integration: * - Multiple forms per Notion workspace * - Connect to existing databases * - Compound workflows (Notion + Slack + CRM + Email) * - Conditional routing based on response content * * @example * ```typescript * import { Typeform } from '@workwayco/integrations/typeform'; * * const typeform = new Typeform({ accessToken: tokens.typeform.access_token }); * * // List all forms * const forms = await typeform.listForms(); * * // Get responses for a form * const responses = await typeform.getResponses({ * formId: 'abc123', * since: '2024-01-01T00:00:00Z', * }); * * // Verify webhook signature * const event = await typeform.verifyWebhook(payload, signature, secret); * ``` */ import { ActionResult } from '@workwayco/sdk'; import { BaseAPIClient } from '../core/index.js'; /** * Typeform integration configuration */ export interface TypeformConfig { /** OAuth access token or personal access token */ accessToken: string; /** Optional: Override API endpoint (for EU data center: https://api.eu.typeform.com) */ apiUrl?: string; /** Request timeout in milliseconds (default: 30000) */ timeout?: number; } /** * Typeform form (typeform) object */ export interface TypeformForm { id: string; title: string; type: 'quiz' | 'form'; workspace: { href: string; }; theme: { href: string; }; settings: TypeformSettings; fields?: TypeformField[]; created_at: string; last_updated_at: string; published_at?: string; _links: { display: string; }; } /** * Form settings */ export interface TypeformSettings { language: string; progress_bar: 'percentage' | 'proportion'; meta: { title?: string; description?: string; allow_indexing?: boolean; }; hide_navigation?: boolean; is_public?: boolean; is_trial?: boolean; show_progress_bar?: boolean; show_typeform_branding?: boolean; are_uploads_public?: boolean; show_time_to_complete?: boolean; show_number_of_submissions?: boolean; show_cookie_consent?: boolean; show_question_number?: boolean; show_key_hint_on_choices?: boolean; autosave_progress?: boolean; free_form_navigation?: boolean; pro_subdomain_enabled?: boolean; capabilities?: { e2e_encryption?: boolean; }; } /** * Form field definition */ export interface TypeformField { id: string; ref: string; title: string; type: TypeformFieldType; properties?: Record; validations?: { required?: boolean; max_length?: number; min_value?: number; max_value?: number; }; attachment?: { type: 'image' | 'video'; href: string; }; } /** * Supported field types */ export type TypeformFieldType = 'short_text' | 'long_text' | 'email' | 'number' | 'dropdown' | 'multiple_choice' | 'yes_no' | 'legal' | 'rating' | 'opinion_scale' | 'ranking' | 'picture_choice' | 'date' | 'file_upload' | 'payment' | 'website' | 'phone_number' | 'calendly' | 'group' | 'statement' | 'matrix'; /** * Form response (submission) */ export interface TypeformResponse { /** Response ID (also called token) */ response_id: string; /** Unique submission identifier */ token: string; /** When respondent landed on the form */ landed_at: string; /** When response was submitted */ submitted_at: string; /** Response metadata */ metadata: TypeformResponseMetadata; /** Answers to questions */ answers: TypeformAnswer[]; /** Hidden field values */ hidden?: Record; /** Calculated fields (e.g., quiz score) */ calculated?: { score?: number; }; /** Variables */ variables?: TypeformVariable[]; } /** * Response metadata */ export interface TypeformResponseMetadata { user_agent: string; platform: 'mobile' | 'tablet' | 'desktop' | 'other'; referer: string; network_id?: string; browser?: string; } /** * Answer object */ export interface TypeformAnswer { field: { id: string; ref: string; type: TypeformFieldType; }; type: TypeformAnswerType; text?: string; email?: string; number?: number; boolean?: boolean; date?: string; file_url?: string; url?: string; phone_number?: string; choice?: TypeformChoice; choices?: { ids?: string[]; labels?: string[]; refs?: string[]; other?: string; }; payment?: { amount: string; last4: string; name: string; success: boolean; }; } /** * Answer types */ export type TypeformAnswerType = 'text' | 'email' | 'number' | 'boolean' | 'date' | 'file_url' | 'url' | 'phone_number' | 'choice' | 'choices' | 'payment'; /** * Choice answer */ export interface TypeformChoice { id?: string; label: string; ref?: string; other?: string; } /** * Variable */ export interface TypeformVariable { key: string; type: 'number' | 'text'; number?: number; text?: string; } /** * List responses result */ export interface TypeformResponsesResult { total_items: number; page_count: number; items: TypeformResponse[]; } /** * Webhook configuration */ export interface TypeformWebhook { id: string; form_id: string; tag: string; url: string; enabled: boolean; secret?: string; verify_ssl?: boolean; created_at: string; updated_at: string; } /** * Webhook event payload */ export interface TypeformWebhookEvent { event_id: string; event_type: 'form_response'; form_response: { form_id: string; token: string; landed_at: string; submitted_at: string; definition: { id: string; title: string; fields: TypeformField[]; }; answers: TypeformAnswer[]; hidden?: Record; calculated?: { score?: number; }; variables?: TypeformVariable[]; }; } /** * Workspace */ export interface TypeformWorkspace { id: string; name: string; default: boolean; shared: boolean; forms: { count: number; href: string; }; self: { href: string; }; } /** * Options for listing forms */ export interface ListFormsOptions { /** Filter by workspace ID */ workspaceId?: string; /** Search query */ search?: string; /** Page number (starts at 1) */ page?: number; /** Results per page (default: 10, max: 200) */ pageSize?: number; } /** * Options for getting responses */ export interface GetResponsesOptions { /** Form ID (required) */ formId: string; /** Max responses per request (default: 25, max: 1000) */ pageSize?: number; /** Filter responses after this date (ISO 8601 or Unix timestamp) */ since?: string; /** Filter responses before this date (ISO 8601 or Unix timestamp) */ until?: string; /** Pagination cursor - get responses after this token */ after?: string; /** Pagination cursor - get responses before this token */ before?: string; /** Filter by response type */ responseType?: 'started' | 'partial' | 'completed'; /** Specific response IDs to include */ includedResponseIds?: string[]; /** Specific response IDs to exclude */ excludedResponseIds?: string[]; /** Search query across all answers */ query?: string; /** Only return answers for these field IDs */ fields?: string[]; /** Only include responses that answered these fields */ answeredFields?: string[]; /** Sort order: field_id,asc or field_id,desc */ sort?: string; } /** * Options for creating a webhook */ export interface CreateWebhookOptions { /** Form ID */ formId: string; /** Webhook tag (identifier) */ tag: string; /** URL to receive webhook events */ url: string; /** Enable/disable webhook (default: true) */ enabled?: boolean; /** Secret for signature verification */ secret?: string; /** Verify SSL certificate (default: true) */ verifySsl?: boolean; } /** * Typeform Integration * * Weniger, aber besser: Forms and responses API with webhook support * for compound workflows. */ export declare class Typeform extends BaseAPIClient { constructor(config: TypeformConfig); /** * List all forms */ listForms(options?: ListFormsOptions): Promise>; /** * Get a form by ID */ getForm(formId: string): Promise>; /** * Get responses for a form * * Note: Very recent submissions (within ~30 minutes) may not be included. * For real-time responses, use webhooks. */ getResponses(options: GetResponsesOptions): Promise>; /** * Delete responses from a form */ deleteResponses(formId: string, responseIds: string[]): Promise>; /** * List webhooks for a form */ listWebhooks(formId: string): Promise>; /** * Get a specific webhook */ getWebhook(formId: string, tag: string): Promise>; /** * Create or update a webhook */ createWebhook(options: CreateWebhookOptions): Promise>; /** * Delete a webhook */ deleteWebhook(formId: string, tag: string): Promise>; /** * List workspaces */ listWorkspaces(): Promise>; /** * Verify and parse a webhook event * * Typeform signs webhooks with HMAC SHA-256 in base64. * Header format: sha256={base64_signature} * * @param payload - Raw request body as string * @param signature - Value of Typeform-Signature header * @param secret - Webhook secret configured when creating the webhook */ verifyWebhook(payload: string, signature: string, secret: string): Promise>; /** * Parse webhook event without verification * Use only for development/testing */ parseWebhookUnsafe(payload: string): ActionResult; /** * Extract answers as a simple key-value object * * Zuhandenheit: Developer thinks "get form data as object" * not "iterate answers, check types, extract values" */ static extractAnswers(response: TypeformResponse | TypeformWebhookEvent['form_response']): Record; /** * Get a specific answer by field ref or ID */ static getAnswer(response: TypeformResponse | TypeformWebhookEvent['form_response'], fieldRefOrId: string): TypeformAnswer | undefined; /** * Compute HMAC SHA-256 signature in base64 (Typeform's format) */ private computeHmacSha256Base64; /** * Get capabilities for Typeform actions */ private getCapabilities; } //# sourceMappingURL=index.d.ts.map