diff --git a/components/fathom/actions/get-recording-summary/get-recording-summary.mjs b/components/fathom/actions/get-recording-summary/get-recording-summary.mjs new file mode 100644 index 0000000000000..fe1108f1f7e59 --- /dev/null +++ b/components/fathom/actions/get-recording-summary/get-recording-summary.mjs @@ -0,0 +1,31 @@ +import fathom from "../../fathom.app.mjs"; + +export default { + key: "fathom-get-recording-summary", + name: "Get Recording Summary", + description: "Get the summary of a recording. [See the documentation](https://developers.fathom.ai/api-reference/recordings/get-summary)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + fathom, + recordingId: { + propDefinition: [ + fathom, + "recordingId", + ], + }, + }, + async run({ $ }) { + const response = await this.fathom.getRecordingSummary({ + $, + recordingId: this.recordingId, + }); + $.export("$summary", `Successfully fetched recording summary for recording ID: ${this.recordingId}`); + return response; + }, +}; diff --git a/components/fathom/actions/get-recording-transcript/get-recording-transcript.mjs b/components/fathom/actions/get-recording-transcript/get-recording-transcript.mjs new file mode 100644 index 0000000000000..e220cc89ff13a --- /dev/null +++ b/components/fathom/actions/get-recording-transcript/get-recording-transcript.mjs @@ -0,0 +1,31 @@ +import fathom from "../../fathom.app.mjs"; + +export default { + key: "fathom-get-recording-transcript", + name: "Get Recording Transcript", + description: "Get the transcript of a recording. [See the documentation](https://developers.fathom.ai/api-reference/recordings/get-transcript)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + fathom, + recordingId: { + propDefinition: [ + fathom, + "recordingId", + ], + }, + }, + async run({ $ }) { + const response = await this.fathom.getRecordingTranscript({ + $, + recordingId: this.recordingId, + }); + $.export("$summary", `Successfully fetched recording transcript for recording ID: ${this.recordingId}`); + return response; + }, +}; diff --git a/components/fathom/actions/list-meetings/list-meetings.mjs b/components/fathom/actions/list-meetings/list-meetings.mjs new file mode 100644 index 0000000000000..ad7d115cafa88 --- /dev/null +++ b/components/fathom/actions/list-meetings/list-meetings.mjs @@ -0,0 +1,61 @@ +import fathom from "../../fathom.app.mjs"; + +export default { + key: "fathom-list-meetings", + name: "List Meetings", + description: "List meetings. [See the documentation](https://developers.fathom.ai/api-reference/meetings/list-meetings)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + fathom, + includeActionItems: { + propDefinition: [ + fathom, + "includeActionItems", + ], + }, + includeCrmMatches: { + propDefinition: [ + fathom, + "includeCrmMatches", + ], + }, + createdAfter: { + type: "string", + label: "Created After", + description: "Filter to meetings with created_at after this timestamp, e.g. `2025-01-01T00:00:00Z`", + optional: true, + }, + createdBefore: { + type: "string", + label: "Created Before", + description: "Filter to meetings with created_at before this timestamp, e.g. `2025-01-01T00:00:00Z`", + optional: true, + }, + cursor: { + type: "string", + label: "Cursor", + description: "If continuing a previous request, the cursor to start from", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.fathom.listMeetings({ + $, + params: { + include_action_items: this.includeActionItems, + include_crm_matches: this.includeCrmMatches, + created_after: this.createdAfter, + created_before: this.createdBefore, + cursor: this.cursor, + }, + }); + $.export("$summary", `Successfully listed ${response?.items?.length} meetings`); + return response; + }, +}; diff --git a/components/fathom/fathom.app.mjs b/components/fathom/fathom.app.mjs index 30bc2ec297ee2..196008ec3a5a7 100644 --- a/components/fathom/fathom.app.mjs +++ b/components/fathom/fathom.app.mjs @@ -1,11 +1,101 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "fathom", - propDefinitions: {}, + propDefinitions: { + recordingId: { + type: "string", + label: "Recording ID", + description: "The ID of a recording", + async options({ prevContext }) { + const { + items, next_cursor: next, + } = await this.listMeetings({ + params: { + cursor: prevContext?.cursor, + }, + }); + return { + options: items.map(({ + recording_id: value, title: label, + }) => ({ + label, + value, + })), + context: { + cursor: next, + }, + }; + }, + }, + includeActionItems: { + type: "boolean", + label: "Include Action Items", + description: "Include the action items for each meeting", + optional: true, + }, + includeCrmMatches: { + type: "boolean", + label: "Include CRM Matches", + description: "Include CRM matches for each meeting. Only returns data from your or your team's linked CRM.", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.fathom.ai/external/v1"; + }, + _makeRequest({ + $ = this, + path, + ...opts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + ...opts, + }); + }, + createWebhook(opts = {}) { + return this._makeRequest({ + path: "/webhooks", + method: "POST", + ...opts, + }); + }, + deleteWebhook({ + webhookId, ...opts + }) { + return this._makeRequest({ + path: `/webhooks/${webhookId}`, + method: "DELETE", + ...opts, + }); + }, + listMeetings(opts = {}) { + return this._makeRequest({ + path: "/meetings", + ...opts, + }); + }, + getRecordingSummary({ + recordingId, ...opts + }) { + return this._makeRequest({ + path: `/recordings/${recordingId}/summary`, + ...opts, + }); + }, + getRecordingTranscript({ + recordingId, ...opts + }) { + return this._makeRequest({ + path: `/recordings/${recordingId}/transcript`, + ...opts, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/fathom/package.json b/components/fathom/package.json index 9ef682f05bc5c..49ec7708b5110 100644 --- a/components/fathom/package.json +++ b/components/fathom/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/fathom", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Fathom Components", "main": "fathom.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0" } -} \ No newline at end of file +} diff --git a/components/fathom/sources/common/base-webhook.mjs b/components/fathom/sources/common/base-webhook.mjs new file mode 100644 index 0000000000000..d265b871ca212 --- /dev/null +++ b/components/fathom/sources/common/base-webhook.mjs @@ -0,0 +1,65 @@ +import fathom from "../../fathom.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + props: { + fathom, + db: "$.service.db", + http: "$.interface.http", + include: { + type: "string[]", + label: "Include Fields", + description: "Fields to include in the webhook payload", + options: [ + "action_items", + "crm_matches", + "summary", + "transcript", + ], + }, + }, + hooks: { + async activate() { + const { id } = await this.fathom.createWebhook({ + data: { + destination_url: this.http.endpoint, + ...this.getWebhookData(), + }, + }); + + this._setWebhookId(id); + }, + async deactivate() { + const webhookId = this._getWebhookId(); + if (webhookId) { + await this.fathom.deleteWebhook({ + webhookId, + }); + } + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(webhookId) { + this.db.set("webhookId", webhookId); + }, + getWebhookData() { + throw new ConfigurationError("getWebhookData is not implemented"); + }, + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + }, + async run(event) { + const { body } = event; + + if (!body) { + return; + } + + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, +}; diff --git a/components/fathom/sources/new-recording/new-recording.mjs b/components/fathom/sources/new-recording/new-recording.mjs new file mode 100644 index 0000000000000..edc940bc600e8 --- /dev/null +++ b/components/fathom/sources/new-recording/new-recording.mjs @@ -0,0 +1,46 @@ +import common from "../common/base-webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "fathom-new-recording", + name: "New Recording (Instant)", + description: "Emit new event when a new recording is created. [See the documentation](https://developers.fathom.ai/api-reference/webhooks/create-a-webhook)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + triggeredFor: { + type: "string[]", + label: "Triggered For", + description: "The types of recordings to trigger the webhook", + options: [ + "my_recordings", + "shared_external_recordings", + "my_shared_with_team_recordings", + "shared_team_recordings", + ], + }, + }, + methods: { + ...common.methods, + getWebhookData() { + return { + triggered_for: this.triggeredFor, + include_action_items: this.include.includes("action_items"), + include_crm_matches: this.include.includes("crm_matches"), + include_summary: this.include.includes("summary"), + include_transcript: this.include.includes("transcript"), + }; + }, + generateMeta(event) { + return { + id: event.recording_id, + summary: `New recording: ${event.recording_id}`, + ts: Date.now(), + }; + }, + }, + sampleEmit, +}; diff --git a/components/fathom/sources/new-recording/test-event.mjs b/components/fathom/sources/new-recording/test-event.mjs new file mode 100644 index 0000000000000..4e3d1c208cf91 --- /dev/null +++ b/components/fathom/sources/new-recording/test-event.mjs @@ -0,0 +1,55 @@ +export default { + "action_items": [], + "calendar_invitees": [ + { + "email": "jane.doe@acme.com", + "email_domain": "acme.com", + "is_external": false, + "matched_speaker_display_name": null, + "name": "Jane Doe" + } + ], + "calendar_invitees_domains_type": "one_or_more_external", + "created_at": "2025-10-15T17:31:26Z", + "crm_matches": { + "error": "No CRM connected" + }, + "default_summary": { + "markdown_formatted": "## Summary\nWe reviewed Q1 OKRs, identified budget risks, and agreed to revisit projections next month.\n", + "template_name": "General" + }, + "meeting_title": "Test call", + "recorded_by": { + "email": "jane.doe@acme.com", + "email_domain": "acme.com", + "name": "Jane Doe", + "team": null + }, + "recording_end_time": "2025-10-15T17:31:19Z", + "recording_id": 94413018, + "recording_start_time": "2025-10-15T17:30:40Z", + "scheduled_end_time": "2025-10-15T17:44:39Z", + "scheduled_start_time": "2025-10-15T17:29:39Z", + "share_url": "https://fathom.video/share/uVksz531ove4z2GUMz1sjZmcySYezP4m", + "title": "Test call", + "transcript": [ + { + "speaker": { + "display_name": "Emmily Bowman", + "matched_calendar_invitee_email": null + }, + "text": "If you don't, you'll see a Fathom panel also on your screen with a start recording button.", + "timestamp": "00:00:03" + }, + { + "speaker": { + "display_name": "Emmily Bowman", + "matched_calendar_invitee_email": null + }, + "text": "Go ahead and click that button so that Fathom can join this meeting now.", + "timestamp": "00:00:09" + }, + ], + "transcript_language": "unknown", + "url": "https://fathom.video/calls/443282345" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e803108c5fd3b..70efc0da7b158 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1934,8 +1934,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/brainbase_labs: - specifiers: {} + components/brainbase_labs: {} components/brainshop: {} @@ -4876,7 +4875,11 @@ importers: components/fastfield_mobile_forms: {} - components/fathom: {} + components/fathom: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/fatture_in_cloud: {} @@ -9277,8 +9280,7 @@ importers: specifier: ^8.3.2 version: 8.3.2 - components/n1n: - specifiers: {} + components/n1n: {} components/n8n_io: {} @@ -15217,8 +15219,7 @@ importers: components/uproc: {} - components/ups: - specifiers: {} + components/ups: {} components/upstash_redis: dependencies: @@ -16150,8 +16151,7 @@ importers: specifier: ^3.1.0 version: 3.1.0 - components/xola: - specifiers: {} + components/xola: {} components/xperiencify: dependencies: