Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,18 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,31 @@
# Hedy Piece for ActivePieces
AI-powered meeting intelligence integration for ActivePieces.
## Features
- Retrieve meeting sessions, highlights, todos, and topics from Hedy.
- Trigger flows when sessions end, highlights are created, or todos are exported.
- Support for pagination and Zapier-compatible response formatting.
- Optional webhook signature verification for additional security.
## Authentication
1. Sign in to your Hedy dashboard.
2. Navigate to **Settings → API**.
3. Generate a new API key (it starts with `hedy_live_`).
4. Paste the key into the Hedy piece connection in ActivePieces.
## Example Automations
- **Post Session Recaps** Trigger on `Session Ended` and send meeting notes to Slack, Microsoft Teams, or email.
- **Sync Todos** When a todo is exported from Hedy, automatically create matching tasks in Asana, Todoist, or ClickUp.
- **Daily Digest** Schedule a flow that lists sessions from the last 24 hours, summarises them with AI, and emails the highlights to your team.
## Error Handling
The Hedy piece provides descriptive errors for common issues such as:
- Invalid API keys or failed authentication.
- Webhook limits being reached (Hedy allows 10 concurrent webhooks per workspace).
- Invalid webhook URLs (public HTTPS endpoints are required for production).
## Support
- Hedy API documentation: https://api.hedy.bot/docs
- Hedy support: support@hedy.bot
- ActivePieces community: https://www.activepieces.com/discord

View File

@@ -0,0 +1,10 @@
{
"name": "@activepieces/piece-hedy",
"version": "0.0.2",
"type": "commonjs",
"main": "./src/index.js",
"types": "./src/index.d.ts",
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-hedy",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/hedy/src",
"projectType": "library",
"release": {
"version": {
"manifestRootsToUpdate": [
"dist/{projectRoot}"
],
"currentVersionResolver": "git-tag",
"fallbackCurrentVersionResolver": "disk"
}
},
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/hedy",
"tsConfig": "packages/pieces/community/hedy/tsconfig.lib.json",
"packageJson": "packages/pieces/community/hedy/package.json",
"main": "packages/pieces/community/hedy/src/index.ts",
"assets": [
"packages/pieces/community/hedy/*.md",
{
"input": "packages/pieces/community/hedy/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
},
"dependsOn": [
"^build",
"prebuild"
]
},
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
},
"prebuild": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/pieces/community/hedy",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
}
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "AI-gestützte MeetingIntelligenz sei die hellste Person im Raum.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Generieren Sie einen API-Schlüssel aus Ihrem Hedy-Dashboard unter Einstellungen → API, fügen Sie dann die Taste hier ein (er beginnt mit `hedy_live_`).",
"Get Session": "Sitzung abrufen",
"List Sessions": "Sessions auflisten",
"Get Highlight": "Hervorheben",
"List Highlights": "Markierungen auflisten",
"List Todos": "List Todos",
"List Session Todos": "Auflistung von Sitzungs-Todos",
"Get Topic": "Thema erhalten",
"List Topics": "Themen auflisten",
"List Topic Sessions": "Themensitzungen auflisten",
"Retrieve a specific session by ID.": "Abrufen einer bestimmten Sitzung per ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Mehrere Sitzungen mit optionaler Themenfilterung und Seiteninierung abrufen.",
"Retrieve a specific highlight by ID.": "Rufen Sie ein spezifisches Highlight per ID ab.",
"Retrieve highlights with optional topic filtering and pagination.": "Highlights mit optionaler Themenfilterung und Seiteninierung abrufen.",
"Retrieve todos assigned to you in Hedy.": "In Hedy zugewiesene Todos abrufen.",
"Retrieve todos generated for a specific session.": "Abrufen von Todos für eine bestimmte Sitzung.",
"Retrieve details for a specific topic.": "Ruft Details zu einem bestimmten Thema ab.",
"Retrieve all topics from your Hedy workspace.": "Rufen Sie alle Themen aus Ihrem Hedy-Arbeitsbereich ab.",
"Retrieve sessions associated with a specific topic.": "Abrufen von Sessionen, die einem bestimmten Thema zugeordnet sind.",
"Session ID": "Sitzungs-ID",
"Return All": "Alle zurückgeben",
"Limit": "Limit",
"Response Format": "Antwortformat",
"Topic": "Thema",
"After Cursor": "Nach Cursor",
"Before Cursor": "Vor dem Cursor",
"Highlight ID": "ID hervorheben",
"Topic ID": "Themen-ID",
"The session ID as shown in the Hedy dashboard.": "Die Session-ID wie im Hedy-Dashboard angezeigt.",
"Return all results instead of using the limit.": "Gibt alle Ergebnisse anstelle des Limits zurück.",
"Maximum number of results to return (default 50).": "Maximale Anzahl der zurückzugebenden Ergebnisse (Standard 50).",
"Select the response format to use.": "Wählen Sie das zu verwendende Antwortformat aus.",
"Optionally filter results by a specific topic.": "Filtern Sie die Ergebnisse nach einem bestimmten Thema.",
"Pagination cursor used to fetch results after a specific item.": "Mit dem Cursor der Pagination werden Ergebnisse nach einem bestimmten Element abgerufen.",
"Pagination cursor used to fetch results before a specific item.": "Mit dem Cursor der Pagination werden Ergebnisse vor einem bestimmten Element abgerufen.",
"The highlight ID as shown in the Hedy dashboard.": "Die Highlight ID wie im Hedy Dashboard angezeigt.",
"The topic ID as shown in the Hedy dashboard.": "Die Themen-ID wie im Hedy-Dashboard angezeigt.",
"Standard": "Standard",
"Zapier Compatible": "Zapier kompatibel",
"Session Created": "Sitzung erstellt",
"Session Ended": "Sitzung beendet",
"Highlight Created": "Hervorhebung erstellt",
"Todo Exported": "Todo exportiert",
"Triggers when a new session is created in Hedy.": "Wird ausgelöst, wenn eine neue Sitzung in Hedy erstellt wird.",
"Triggers when a session is completed in Hedy.": "Wird ausgelöst, wenn eine Sitzung in Hedy abgeschlossen ist.",
"Triggers when a highlight is created during a session.": "Wird ausgelöst, wenn während einer Sitzung ein Highlight erstellt wird.",
"Triggers when a todo item is exported from Hedy.": "Wird ausgelöst, wenn ein Todo-Item von Hedy exportiert wird.",
"Verify Signature": "Signatur überprüfen",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Verifizieren Sie die Webhook-Signatur mit dem von Hedy zurückgegebenen Geheimnis. Deaktivieren Sie diese Option, wenn Hedy kein Geheimnis für Ihre Anmeldung zur Verfügung stellt."
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "La inteligencia de reuniones de la AI: sea la persona más brillante de la sala.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Genera una clave API desde su panel de control de Hedy en Configuración → API, luego pegue la clave aquí (comienza con `hedy_live_`).",
"Get Session": "Obtener sesión",
"List Sessions": "Listar Sesiones",
"Get Highlight": "Resaltar",
"List Highlights": "Lista de destacados",
"List Todos": "List Todos",
"List Session Todos": "Listar Todos de Sesiones",
"Get Topic": "Obtener Tema",
"List Topics": "Temas de lista",
"List Topic Sessions": "Listar sesiones de temas",
"Retrieve a specific session by ID.": "Recuperar una sesión específica por ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Recuperar múltiples sesiones con filtrado opcional de temas y paginación.",
"Retrieve a specific highlight by ID.": "Recuperar un resaltado específico por ID.",
"Retrieve highlights with optional topic filtering and pagination.": "Recuperar resaltados con filtrado opcional de temas y paginación.",
"Retrieve todos assigned to you in Hedy.": "Recuperar tareas asignadas en Hedy.",
"Retrieve todos generated for a specific session.": "Recuperar todo generado para una sesión específica.",
"Retrieve details for a specific topic.": "Recuperar detalles de un tema específico.",
"Retrieve all topics from your Hedy workspace.": "Recuperar todos los temas de su espacio de trabajo Hedy.",
"Retrieve sessions associated with a specific topic.": "Recuperar sesiones asociadas con un tema específico.",
"Session ID": "ID de sesión",
"Return All": "Devolver todos",
"Limit": "Límite",
"Response Format": "Formato de respuesta",
"Topic": "Tema",
"After Cursor": "Después del cursor",
"Before Cursor": "Antes del Cursor",
"Highlight ID": "Resaltar ID",
"Topic ID": "ID del tema",
"The session ID as shown in the Hedy dashboard.": "El ID de sesión como se muestra en el panel de control de Hedy.",
"Return all results instead of using the limit.": "Devuelve todos los resultados en lugar de usar el límite.",
"Maximum number of results to return (default 50).": "Número máximo de resultados a devolver (por defecto 50).",
"Select the response format to use.": "Seleccione el formato de respuesta a utilizar.",
"Optionally filter results by a specific topic.": "Opcionalmente filtrar resultados por un tema específico.",
"Pagination cursor used to fetch results after a specific item.": "cursor de paginación usado para obtener resultados después de un elemento específico.",
"Pagination cursor used to fetch results before a specific item.": "cursor de paginación usado para obtener resultados antes de un elemento específico.",
"The highlight ID as shown in the Hedy dashboard.": "El ID de resaltado como se muestra en el panel de control de Hedy.",
"The topic ID as shown in the Hedy dashboard.": "El ID del tema como se muestra en el panel de control de Hedy.",
"Standard": "Estándar",
"Zapier Compatible": "Compatible con Zapier",
"Session Created": "Sesión creada",
"Session Ended": "Sesión finalizada",
"Highlight Created": "Resaltar creado",
"Todo Exported": "Tarea exportada",
"Triggers when a new session is created in Hedy.": "Se activa cuando se crea una nueva sesión en Hedy.",
"Triggers when a session is completed in Hedy.": "Dispara cuando una sesión se completa en Hedy.",
"Triggers when a highlight is created during a session.": "Dispara cuando se crea un resaltado durante una sesión.",
"Triggers when a todo item is exported from Hedy.": "Dispara cuando un elemento de tarea es exportado desde Hedy.",
"Verify Signature": "Verificar Firma",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Verifique la firma del webhook usando el secreto devuelto por Hedy. Deshabilite esta opción si Hedy no proporciona un secreto de firma para su cuenta."
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "Lintelligence de réunion de lIA soyez la personne la plus brillante dans la salle.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Générez une clé API à partir de votre tableau de bord Hedy sous Paramètres → API, puis collez la clé ici (elle commence par `hedy_live_`).",
"Get Session": "Obtenir une session",
"List Sessions": "Lister les sessions",
"Get Highlight": "Obtenir la surbrillance",
"List Highlights": "Liste des points forts",
"List Todos": "List Todos",
"List Session Todos": "Liste des todos de session",
"Get Topic": "Obtenir le sujet",
"List Topics": "Liste des sujets",
"List Topic Sessions": "Lister les sessions de sujet",
"Retrieve a specific session by ID.": "Récupérer une session spécifique par ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Récupère plusieurs sessions avec filtrage et pagination facultatifs des sujets.",
"Retrieve a specific highlight by ID.": "Récupérer une surbrillance spécifique par ID.",
"Retrieve highlights with optional topic filtering and pagination.": "Récupérer les surlignements avec filtrage facultatif des sujets et de la pagination.",
"Retrieve todos assigned to you in Hedy.": "Récupérez les todos qui vous sont assignées à Hedy.",
"Retrieve todos generated for a specific session.": "Récupérer les todos générées pour une session spécifique.",
"Retrieve details for a specific topic.": "Récupérer les détails d'un sujet spécifique.",
"Retrieve all topics from your Hedy workspace.": "Récupérer tous les sujets de votre espace de travail Hedy",
"Retrieve sessions associated with a specific topic.": "Récupérer les sessions associées à un sujet spécifique.",
"Session ID": "ID de session",
"Return All": "Retourner tout",
"Limit": "Limite",
"Response Format": "Format de réponse",
"Topic": "Sujet",
"After Cursor": "Après le curseur",
"Before Cursor": "Avant le curseur",
"Highlight ID": "Identifiant de surbrillance",
"Topic ID": "ID du sujet",
"The session ID as shown in the Hedy dashboard.": "L'ID de la session comme indiqué dans le tableau de bord Hedy",
"Return all results instead of using the limit.": "Renvoie tous les résultats au lieu d'utiliser la limite.",
"Maximum number of results to return (default 50).": "Nombre maximum de résultats à retourner (par défaut 50).",
"Select the response format to use.": "Sélectionnez le format de réponse à utiliser.",
"Optionally filter results by a specific topic.": "Filtrer éventuellement les résultats par sujet spécifique.",
"Pagination cursor used to fetch results after a specific item.": "Curseur de pagination utilisé pour récupérer les résultats après un élément spécifique.",
"Pagination cursor used to fetch results before a specific item.": "Curseur de pagination utilisé pour récupérer les résultats avant un élément spécifique.",
"The highlight ID as shown in the Hedy dashboard.": "L'ID de surbrillance comme indiqué dans le tableau de bord Hedy",
"The topic ID as shown in the Hedy dashboard.": "L'ID du sujet tel qu'indiqué dans le tableau de bord Hedy",
"Standard": "Standard",
"Zapier Compatible": "Compatible avec Zapier",
"Session Created": "Session créée",
"Session Ended": "Session terminée",
"Highlight Created": "Surlignage créé",
"Todo Exported": "Todo exportée",
"Triggers when a new session is created in Hedy.": "Déclenche quand une nouvelle session est créée dans Hedy.",
"Triggers when a session is completed in Hedy.": "Déclenche quand une session est terminée à Hedy.",
"Triggers when a highlight is created during a session.": "Déclenche quand une surbrillance est créée pendant une session.",
"Triggers when a todo item is exported from Hedy.": "Déclenche lorsqu'un élément de todo est exporté de Hedy.",
"Verify Signature": "Vérifier la signature",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Vérifiez la signature du webhook en utilisant le secret retourné par Hedy. Désactivez cette option si Hedy ne fournit pas de secret de signature pour votre compte."
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "AIを活用した会議インテリジェンスは、部屋の中で最も明るい人になります。",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Hedy ダッシュボードから API キーを生成するには、設定 → API を選択し、ここにキーを貼り付けます (`hedy_live_`で始まります)。",
"Get Session": "セッションを取得",
"List Sessions": "セッション一覧",
"Get Highlight": "ハイライトを取得",
"List Highlights": "リストのハイライト",
"List Todos": "List Todos",
"List Session Todos": "セッションのタスク一覧",
"Get Topic": "トピックを取得",
"List Topics": "トピック一覧",
"List Topic Sessions": "トピックセッションの一覧",
"Retrieve a specific session by ID.": "IDで特定のセッションを取得します。",
"Retrieve multiple sessions with optional topic filtering and pagination.": "オプションのトピックフィルタリングとページネーションで複数のセッションを取得します。",
"Retrieve a specific highlight by ID.": "ID による特定のハイライトを取得します。",
"Retrieve highlights with optional topic filtering and pagination.": "オプションのトピックフィルタリングとページネーションでハイライトを取得します。",
"Retrieve todos assigned to you in Hedy.": "ヘディであなたに割り当てられたタスクを取得します。",
"Retrieve todos generated for a specific session.": "特定のセッションで生成されたタスクを取得します。",
"Retrieve details for a specific topic.": "特定のトピックの詳細を取得します。",
"Retrieve all topics from your Hedy workspace.": "Hedy ワークスペースからすべてのトピックを取得します。",
"Retrieve sessions associated with a specific topic.": "特定のトピックに関連付けられているセッションを取得します。",
"Session ID": "セッションID",
"Return All": "すべて戻る",
"Limit": "制限",
"Response Format": "応答形式",
"Topic": "トピック",
"After Cursor": "カーソルの後",
"Before Cursor": "カーソルの前",
"Highlight ID": "IDを強調表示",
"Topic ID": "トピックID",
"The session ID as shown in the Hedy dashboard.": "Hedyダッシュボードに表示されるセッションID。",
"Return all results instead of using the limit.": "制限を使用せずにすべての結果を返します。",
"Maximum number of results to return (default 50).": "返す結果の最大数デフォルトは50。",
"Select the response format to use.": "使用する応答形式を選択します。",
"Optionally filter results by a specific topic.": "オプションで特定のトピックで結果をフィルタリングします。",
"Pagination cursor used to fetch results after a specific item.": "特定のアイテムの後に結果を取得するために使用されるページネーションカーソル。",
"Pagination cursor used to fetch results before a specific item.": "特定のアイテムの前に結果を取得するために使用されるページネーションカーソル。",
"The highlight ID as shown in the Hedy dashboard.": "Hedyダッシュボードに表示されるハイライトID。",
"The topic ID as shown in the Hedy dashboard.": "Hedy ダッシュボードに表示されるトピック ID 。",
"Standard": "標準",
"Zapier Compatible": "Zapier 互換性あり",
"Session Created": "セッションが作成されました",
"Session Ended": "セッション終了",
"Highlight Created": "作成されたハイライト",
"Todo Exported": "Todo がエクスポートされました",
"Triggers when a new session is created in Hedy.": "Hedy で新しいセッションが作成されたときにトリガーします。",
"Triggers when a session is completed in Hedy.": "Hedy でセッションが完了したときにトリガーします。",
"Triggers when a highlight is created during a session.": "セッション中にハイライトが作成されたときにトリガーされます。",
"Triggers when a todo item is exported from Hedy.": "Hedy からタスクアイテムをエクスポートしたときにトリガーします。",
"Verify Signature": "署名の確認",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Hedy が返却したシークレットを使用して Webhook の署名を確認します。Hedy があなたのアカウントの署名の秘密を提供していない場合は、このオプションを無効にします。"
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "AI-aangedreven meetingintelligentie wees de mooiste persoon in de kamer.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Genereer een API-sleutel van uw Hedy dashboard onder Instellingen → API, plak vervolgens de sleutel hier (het begint met `hedy_live_`).",
"Get Session": "Sessie opvragen",
"List Sessions": "Lijst van sessies",
"Get Highlight": "Krijg accentuering",
"List Highlights": "Lijst accentueringen",
"List Todos": "List Todos",
"List Session Todos": "Sessie totaal weergeven",
"Get Topic": "Topic opvragen",
"List Topics": "Onderwerpen weergeven",
"List Topic Sessions": "Lijst van onderwerpsessies",
"Retrieve a specific session by ID.": "Ophalen van een specifieke sessie met ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Haal meerdere sessies op met optionele topic filtering en paginering.",
"Retrieve a specific highlight by ID.": "Ophalen van een specifieke markering door ID.",
"Retrieve highlights with optional topic filtering and pagination.": "Ophalen hoogtepunten met optionele topic filtering en paginering.",
"Retrieve todos assigned to you in Hedy.": "Ophalen van alle aan u toegewezen taken in Hedy.",
"Retrieve todos generated for a specific session.": "Ophalen todos gegenereerd voor een specifieke sessie.",
"Retrieve details for a specific topic.": "Haal details op voor een specifiek onderwerp.",
"Retrieve all topics from your Hedy workspace.": "Haal alle onderwerpen op van je Hedy workspace.",
"Retrieve sessions associated with a specific topic.": "Ophalen van sessies die gekoppeld zijn aan een specifiek onderwerp.",
"Session ID": "Sessie ID",
"Return All": "Retourneer alles",
"Limit": "Limiet",
"Response Format": "Antwoord formaat",
"Topic": "Onderwerp",
"After Cursor": "Na cursor",
"Before Cursor": "Voor cursor",
"Highlight ID": "ID markeren",
"Topic ID": "Onderwerp ID",
"The session ID as shown in the Hedy dashboard.": "De sessie-ID zoals weergegeven in het Hedy dashboard.",
"Return all results instead of using the limit.": "Resultaat is alle resultaten in plaats van het gebruik van de limiet.",
"Maximum number of results to return (default 50).": "Maximum aantal resultaten om terug te keren (standaard 50).",
"Select the response format to use.": "Selecteer het antwoordformaat om te gebruiken.",
"Optionally filter results by a specific topic.": "Filter de resultaten optioneel op een specifiek onderwerp.",
"Pagination cursor used to fetch results after a specific item.": "Paginering cursor wordt gebruikt om resultaten op te halen na een specifiek item.",
"Pagination cursor used to fetch results before a specific item.": "Paginering cursor wordt gebruikt om resultaten op te halen voor een specifiek item.",
"The highlight ID as shown in the Hedy dashboard.": "De markeerstitel-ID zoals getoond in het Hedy dashboard.",
"The topic ID as shown in the Hedy dashboard.": "Het onderwerp ID zoals getoond in het Hedy dashboard.",
"Standard": "Standaard",
"Zapier Compatible": "Zapier Compatibel",
"Session Created": "Sessie aangemaakt",
"Session Ended": "Sessie beëindigd",
"Highlight Created": "Markeer Aangemaakt",
"Todo Exported": "Todo geëxporteerd",
"Triggers when a new session is created in Hedy.": "Triggert wanneer een nieuwe sessie wordt gemaakt in Hedy.",
"Triggers when a session is completed in Hedy.": "Triggert wanneer een sessie is voltooid in Hedy.",
"Triggers when a highlight is created during a session.": "Triggert wanneer een markering wordt gemaakt tijdens een sessie.",
"Triggers when a todo item is exported from Hedy.": "Triggert wanneer een todo-item wordt geëxporteerd vanuit Hedy.",
"Verify Signature": "Verifieer handtekening",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Verifieer de webhook handtekening met behulp van het door Hedy geretourneerde geheim. Schakel deze optie uit als Hedy geen handtekening voor uw account inlevert."
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "Inteligência de reunião com o poder IA — seja a pessoa mais brilhante na sala.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Gerar uma chave de API a partir do seu painel Hedy em Configurações → API, e então cole a chave aqui (ele começa com `hedy_live_`).",
"Get Session": "Obter a sessão",
"List Sessions": "Listar Sessões",
"Get Highlight": "Obter Destaque",
"List Highlights": "Listar destaques",
"List Todos": "List Todos",
"List Session Todos": "Listar todas as sessões",
"Get Topic": "Obter Tópico",
"List Topics": "Listar tópicos",
"List Topic Sessions": "Listar sessões de tópicos",
"Retrieve a specific session by ID.": "Recuperar uma sessão específica por ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Recuperar várias sessões com filtragem e paginação de tópico opcionais.",
"Retrieve a specific highlight by ID.": "Recuperar um destaque específico por ID.",
"Retrieve highlights with optional topic filtering and pagination.": "Recuperar destaques com filtragem e paginação de tópico opcionais.",
"Retrieve todos assigned to you in Hedy.": "Recupere tarefas atribuídas a você em Hedy.",
"Retrieve todos generated for a specific session.": "Recuperar tarefas geradas para uma sessão específica.",
"Retrieve details for a specific topic.": "Recuperar detalhes para um tópico específico.",
"Retrieve all topics from your Hedy workspace.": "Recupere todos os tópicos da sua área de trabalho Hedy.",
"Retrieve sessions associated with a specific topic.": "Recuperar sessões associadas a um tópico específico.",
"Session ID": "ID da sessão",
"Return All": "Devolver tudo",
"Limit": "Limitar",
"Response Format": "Formato de Resposta",
"Topic": "Tópico",
"After Cursor": "Após Cursor",
"Before Cursor": "Antes do Cursor",
"Highlight ID": "Destacar ID",
"Topic ID": "ID do tópico",
"The session ID as shown in the Hedy dashboard.": "O ID de sessão como mostrado no painel Hedy.",
"Return all results instead of using the limit.": "Retornar todos os resultados em vez de usar o limite.",
"Maximum number of results to return (default 50).": "Número máximo de resultados a retornar (padrão 50).",
"Select the response format to use.": "Selecione o formato de resposta a ser usado.",
"Optionally filter results by a specific topic.": "Opcionalmente filtra os resultados por um tópico específico.",
"Pagination cursor used to fetch results after a specific item.": "Cursor de paginação usado para obter resultados após um item específico.",
"Pagination cursor used to fetch results before a specific item.": "Cursor de paginação usado para obter resultados antes de um item específico.",
"The highlight ID as shown in the Hedy dashboard.": "O ID de destaque como mostrado no painel Hedy.",
"The topic ID as shown in the Hedy dashboard.": "O ID do tópico como mostrado no painel Hedy.",
"Standard": "Padrão",
"Zapier Compatible": "Compatível com Zapier",
"Session Created": "Sessão Criada",
"Session Ended": "Sessão Encerrada",
"Highlight Created": "Destacar Criado",
"Todo Exported": "Tarefa exportada",
"Triggers when a new session is created in Hedy.": "Aciona quando uma sessão é criada no Hedy.",
"Triggers when a session is completed in Hedy.": "Dispara quando uma sessão for concluída no Hedy.",
"Triggers when a highlight is created during a session.": "Dispara quando um destaque é criado durante uma sessão.",
"Triggers when a todo item is exported from Hedy.": "Aciona quando um item de tarefa é exportado do Hedy.",
"Verify Signature": "Verificar assinatura",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Verifique a assinatura de webhook usando o segredo retornado pelo Hedy. Desative essa opção se Hedy não fornecer um segredo de assinatura para sua conta."
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "AI-powered meeting intelligence be the brightest person in the room.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).",
"Get Session": "Get Session",
"List Sessions": "List Sessions",
"Get Highlight": "Get Highlight",
"List Highlights": "List Highlights",
"List Todos": "List Todos",
"List Session Todos": "List Session Todos",
"Get Topic": "Get Topic",
"List Topics": "List Topics",
"List Topic Sessions": "List Topic Sessions",
"Retrieve a specific session by ID.": "Retrieve a specific session by ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Retrieve multiple sessions with optional topic filtering and pagination.",
"Retrieve a specific highlight by ID.": "Retrieve a specific highlight by ID.",
"Retrieve highlights with optional topic filtering and pagination.": "Retrieve highlights with optional topic filtering and pagination.",
"Retrieve todos assigned to you in Hedy.": "Retrieve todos assigned to you in Hedy.",
"Retrieve todos generated for a specific session.": "Retrieve todos generated for a specific session.",
"Retrieve details for a specific topic.": "Retrieve details for a specific topic.",
"Retrieve all topics from your Hedy workspace.": "Retrieve all topics from your Hedy workspace.",
"Retrieve sessions associated with a specific topic.": "Retrieve sessions associated with a specific topic.",
"Session ID": "Session ID",
"Return All": "Return All",
"Limit": "Limit",
"Response Format": "Response Format",
"Topic": "Topic",
"After Cursor": "After Cursor",
"Before Cursor": "Before Cursor",
"Highlight ID": "Highlight ID",
"Topic ID": "Topic ID",
"The session ID as shown in the Hedy dashboard.": "The session ID as shown in the Hedy dashboard.",
"Return all results instead of using the limit.": "Return all results instead of using the limit.",
"Maximum number of results to return (default 50).": "Maximum number of results to return (default 50).",
"Select the response format to use.": "Select the response format to use.",
"Optionally filter results by a specific topic.": "Optionally filter results by a specific topic.",
"Pagination cursor used to fetch results after a specific item.": "Pagination cursor used to fetch results after a specific item.",
"Pagination cursor used to fetch results before a specific item.": "Pagination cursor used to fetch results before a specific item.",
"The highlight ID as shown in the Hedy dashboard.": "The highlight ID as shown in the Hedy dashboard.",
"The topic ID as shown in the Hedy dashboard.": "The topic ID as shown in the Hedy dashboard.",
"Standard": "Standard",
"Zapier Compatible": "Zapier Compatible",
"Session Created": "Session Created",
"Session Ended": "Session Ended",
"Highlight Created": "Highlight Created",
"Todo Exported": "Todo Exported",
"Triggers when a new session is created in Hedy.": "Triggers when a new session is created in Hedy.",
"Triggers when a session is completed in Hedy.": "Triggers when a session is completed in Hedy.",
"Triggers when a highlight is created during a session.": "Triggers when a highlight is created during a session.",
"Triggers when a todo item is exported from Hedy.": "Triggers when a todo item is exported from Hedy.",
"Verify Signature": "Verify Signature",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account."
}

View File

@@ -0,0 +1,52 @@
{
"AI-powered meeting intelligence be the brightest person in the room.": "AI-powered meeting intelligence be the brightest person in the room.",
"Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).": "Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).",
"Get Session": "Get Session",
"List Sessions": "List Sessions",
"Get Highlight": "Get Highlight",
"List Highlights": "List Highlights",
"List Todos": "List Todos",
"List Session Todos": "List Session Todos",
"Get Topic": "Get Topic",
"List Topics": "List Topics",
"List Topic Sessions": "List Topic Sessions",
"Retrieve a specific session by ID.": "Retrieve a specific session by ID.",
"Retrieve multiple sessions with optional topic filtering and pagination.": "Retrieve multiple sessions with optional topic filtering and pagination.",
"Retrieve a specific highlight by ID.": "Retrieve a specific highlight by ID.",
"Retrieve highlights with optional topic filtering and pagination.": "Retrieve highlights with optional topic filtering and pagination.",
"Retrieve todos assigned to you in Hedy.": "Retrieve todos assigned to you in Hedy.",
"Retrieve todos generated for a specific session.": "Retrieve todos generated for a specific session.",
"Retrieve details for a specific topic.": "Retrieve details for a specific topic.",
"Retrieve all topics from your Hedy workspace.": "Retrieve all topics from your Hedy workspace.",
"Retrieve sessions associated with a specific topic.": "Retrieve sessions associated with a specific topic.",
"Session ID": "Session ID",
"Return All": "Return All",
"Limit": "Limit",
"Response Format": "Response Format",
"Topic": "Topic",
"After Cursor": "After Cursor",
"Before Cursor": "Before Cursor",
"Highlight ID": "Highlight ID",
"Topic ID": "Topic ID",
"The session ID as shown in the Hedy dashboard.": "The session ID as shown in the Hedy dashboard.",
"Return all results instead of using the limit.": "Return all results instead of using the limit.",
"Maximum number of results to return (default 50).": "Maximum number of results to return (default 50).",
"Select the response format to use.": "Select the response format to use.",
"Optionally filter results by a specific topic.": "Optionally filter results by a specific topic.",
"Pagination cursor used to fetch results after a specific item.": "Pagination cursor used to fetch results after a specific item.",
"Pagination cursor used to fetch results before a specific item.": "Pagination cursor used to fetch results before a specific item.",
"The highlight ID as shown in the Hedy dashboard.": "The highlight ID as shown in the Hedy dashboard.",
"The topic ID as shown in the Hedy dashboard.": "The topic ID as shown in the Hedy dashboard.",
"Standard": "Standard",
"Zapier Compatible": "Zapier Compatible",
"Session Created": "Session Created",
"Session Ended": "Session Ended",
"Highlight Created": "Highlight Created",
"Todo Exported": "Todo Exported",
"Triggers when a new session is created in Hedy.": "Triggers when a new session is created in Hedy.",
"Triggers when a session is completed in Hedy.": "Triggers when a session is completed in Hedy.",
"Triggers when a highlight is created during a session.": "Triggers when a highlight is created during a session.",
"Triggers when a todo item is exported from Hedy.": "Triggers when a todo item is exported from Hedy.",
"Verify Signature": "Verify Signature",
"Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.": "Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account."
}

View File

@@ -0,0 +1,42 @@
import { createPiece } from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { hedyAuth } from './lib/auth';
import {
getSession,
listSessions,
getHighlight,
listHighlights,
listTodos,
listSessionTodos,
getTopic,
listTopics,
listTopicSessions,
} from './lib/actions';
import {
sessionCreated,
sessionEnded,
highlightCreated,
todoExported,
} from './lib/triggers';
export const hedy = createPiece({
displayName: 'Hedy',
description: 'AI-powered meeting intelligence be the brightest person in the room.',
auth: hedyAuth,
minimumSupportedRelease: '0.69.0',
logoUrl: 'https://cdn.activepieces.com/pieces/hedy.png',
categories: [PieceCategory.PRODUCTIVITY, PieceCategory.ARTIFICIAL_INTELLIGENCE],
authors: ['HedyAI'],
actions: [
getSession,
listSessions,
getHighlight,
listHighlights,
listTodos,
listSessionTodos,
getTopic,
listTopics,
listTopicSessions,
],
triggers: [sessionCreated, sessionEnded, highlightCreated, todoExported],
});

View File

@@ -0,0 +1,26 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient, unwrapResource } from '../../common/client';
import { commonProps } from '../../common/props';
import { Highlight } from '../../common/types';
export const getHighlight = createAction({
auth: hedyAuth,
name: 'get-highlight',
displayName: 'Get Highlight',
description: 'Retrieve a specific highlight by ID.',
props: {
highlightId: commonProps.highlightId,
},
async run(context) {
const highlightId = context.propsValue.highlightId as string;
const client = new HedyApiClient(context.auth.secret_text);
const response = await client.request<Highlight>({
method: HttpMethod.GET,
path: `/highlights/${highlightId}`,
});
return unwrapResource(response);
},
});

View File

@@ -0,0 +1,2 @@
export * from './get-highlight';
export * from './list-highlights';

View File

@@ -0,0 +1,42 @@
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient } from '../../common/client';
import { commonProps } from '../../common/props';
import { topicDropdown } from '../../common/load-options';
import { Highlight } from '../../common/types';
import { assertLimit } from '../../common/validation';
export const listHighlights = createAction({
auth: hedyAuth,
name: 'list-highlights',
displayName: 'List Highlights',
description: 'Retrieve highlights with optional topic filtering and pagination.',
props: {
returnAll: commonProps.returnAll,
limit: commonProps.limit,
format: commonProps.format,
topicId: topicDropdown,
after: commonProps.afterCursor,
before: commonProps.beforeCursor,
},
async run(context) {
const client = new HedyApiClient(context.auth.secret_text);
const { returnAll, limit, format, topicId, after, before } = context.propsValue as {
returnAll?: boolean;
limit?: number;
format?: 'standard' | 'zapier';
topicId?: string;
after?: string;
before?: string;
};
return client.paginate<Highlight>('/highlights', {
returnAll: Boolean(returnAll),
limit: assertLimit(limit),
format,
topicId,
after,
before,
});
},
});

View File

@@ -0,0 +1,4 @@
export * from './sessions';
export * from './highlights';
export * from './todos';
export * from './topics';

View File

@@ -0,0 +1,26 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient, unwrapResource } from '../../common/client';
import { commonProps } from '../../common/props';
import { Session } from '../../common/types';
export const getSession = createAction({
auth: hedyAuth,
name: 'get-session',
displayName: 'Get Session',
description: 'Retrieve a specific session by ID.',
props: {
sessionId: commonProps.sessionId,
},
async run(context) {
const sessionId = context.propsValue.sessionId as string;
const client = new HedyApiClient(context.auth.secret_text);
const response = await client.request<Session>({
method: HttpMethod.GET,
path: `/sessions/${sessionId}`,
});
return unwrapResource(response);
},
});

View File

@@ -0,0 +1,2 @@
export * from './get-session';
export * from './list-sessions';

View File

@@ -0,0 +1,42 @@
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient } from '../../common/client';
import { commonProps } from '../../common/props';
import { topicDropdown } from '../../common/load-options';
import { Session } from '../../common/types';
import { assertLimit } from '../../common/validation';
export const listSessions = createAction({
auth: hedyAuth,
name: 'list-sessions',
displayName: 'List Sessions',
description: 'Retrieve multiple sessions with optional topic filtering and pagination.',
props: {
returnAll: commonProps.returnAll,
limit: commonProps.limit,
format: commonProps.format,
topicId: topicDropdown,
after: commonProps.afterCursor,
before: commonProps.beforeCursor,
},
async run(context) {
const client = new HedyApiClient(context.auth.secret_text);
const { returnAll, limit, format, topicId, after, before } = context.propsValue as {
returnAll?: boolean;
limit?: number;
format?: 'standard' | 'zapier';
topicId?: string;
after?: string;
before?: string;
};
return client.paginate<Session>('/sessions', {
returnAll: Boolean(returnAll),
limit: assertLimit(limit),
format,
topicId,
after,
before,
});
},
});

View File

@@ -0,0 +1,2 @@
export * from './list-todos';
export * from './list-session-todos';

View File

@@ -0,0 +1,56 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient } from '../../common/client';
import { commonProps } from '../../common/props';
import { PaginatedResponse, Todo } from '../../common/types';
import { assertLimit } from '../../common/validation';
function toTodoArray(result: unknown): Todo[] {
if (Array.isArray(result)) {
return result as Todo[];
}
if (result && typeof result === 'object' && 'data' in result) {
const data = (result as PaginatedResponse<Todo>).data;
if (Array.isArray(data)) {
return data;
}
}
return [];
}
export const listSessionTodos = createAction({
auth: hedyAuth,
name: 'list-session-todos',
displayName: 'List Session Todos',
description: 'Retrieve todos generated for a specific session.',
props: {
sessionId: commonProps.sessionId,
returnAll: commonProps.returnAll,
limit: commonProps.limit,
},
async run(context) {
const sessionId = context.propsValue.sessionId as string;
const client = new HedyApiClient(context.auth.secret_text);
const { returnAll, limit } = context.propsValue as {
returnAll?: boolean;
limit?: number;
};
const response = await client.request<Todo[]>({
method: HttpMethod.GET,
path: `/sessions/${sessionId}/todos`,
});
const todos = toTodoArray(response);
if (!returnAll) {
const limited = assertLimit(limit);
return limited ? todos.slice(0, limited) : todos.slice(0, 50);
}
return todos;
},
});

View File

@@ -0,0 +1,54 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient } from '../../common/client';
import { commonProps } from '../../common/props';
import { PaginatedResponse, Todo } from '../../common/types';
import { assertLimit } from '../../common/validation';
function extractTodos(result: unknown): Todo[] {
if (Array.isArray(result)) {
return result as Todo[];
}
if (result && typeof result === 'object' && 'data' in result) {
const data = (result as PaginatedResponse<Todo>).data;
if (Array.isArray(data)) {
return data;
}
}
return [];
}
export const listTodos = createAction({
auth: hedyAuth,
name: 'list-todos',
displayName: 'List Todos',
description: 'Retrieve todos assigned to you in Hedy.',
props: {
returnAll: commonProps.returnAll,
limit: commonProps.limit,
},
async run(context) {
const client = new HedyApiClient(context.auth.secret_text);
const { returnAll, limit } = context.propsValue as {
returnAll?: boolean;
limit?: number;
};
const response = await client.request<Todo[]>({
method: HttpMethod.GET,
path: '/todos',
});
const todos = extractTodos(response);
if (!returnAll) {
const limited = assertLimit(limit);
return limited ? todos.slice(0, limited) : todos.slice(0, 50);
}
return todos;
},
});

View File

@@ -0,0 +1,26 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient, unwrapResource } from '../../common/client';
import { commonProps } from '../../common/props';
import { Topic } from '../../common/types';
export const getTopic = createAction({
auth: hedyAuth,
name: 'get-topic',
displayName: 'Get Topic',
description: 'Retrieve details for a specific topic.',
props: {
topicId: commonProps.topicId,
},
async run(context) {
const topicId = context.propsValue.topicId as string;
const client = new HedyApiClient(context.auth.secret_text);
const response = await client.request<Topic>({
method: HttpMethod.GET,
path: `/topics/${topicId}`,
});
return unwrapResource(response);
},
});

View File

@@ -0,0 +1,3 @@
export * from './get-topic';
export * from './list-topics';
export * from './list-topic-sessions';

View File

@@ -0,0 +1,56 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient } from '../../common/client';
import { commonProps } from '../../common/props';
import { PaginatedResponse, Session } from '../../common/types';
import { assertLimit } from '../../common/validation';
function toSessionArray(result: unknown): Session[] {
if (Array.isArray(result)) {
return result as Session[];
}
if (result && typeof result === 'object' && 'data' in result) {
const data = (result as PaginatedResponse<Session>).data;
if (Array.isArray(data)) {
return data;
}
}
return [];
}
export const listTopicSessions = createAction({
auth: hedyAuth,
name: 'list-topic-sessions',
displayName: 'List Topic Sessions',
description: 'Retrieve sessions associated with a specific topic.',
props: {
topicId: commonProps.topicId,
returnAll: commonProps.returnAll,
limit: commonProps.limit,
},
async run(context) {
const topicId = context.propsValue.topicId as string;
const client = new HedyApiClient(context.auth.secret_text);
const { returnAll, limit } = context.propsValue as {
returnAll?: boolean;
limit?: number;
};
const response = await client.request<Session[]>({
method: HttpMethod.GET,
path: `/topics/${topicId}/sessions`,
});
const sessions = toSessionArray(response);
if (!returnAll) {
const limited = assertLimit(limit);
return limited ? sessions.slice(0, limited) : sessions.slice(0, 50);
}
return sessions;
},
});

View File

@@ -0,0 +1,54 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { hedyAuth } from '../../auth';
import { HedyApiClient } from '../../common/client';
import { commonProps } from '../../common/props';
import { PaginatedResponse, Topic } from '../../common/types';
import { assertLimit } from '../../common/validation';
function toTopicArray(result: unknown): Topic[] {
if (Array.isArray(result)) {
return result as Topic[];
}
if (result && typeof result === 'object' && 'data' in result) {
const data = (result as PaginatedResponse<Topic>).data;
if (Array.isArray(data)) {
return data;
}
}
return [];
}
export const listTopics = createAction({
auth: hedyAuth,
name: 'list-topics',
displayName: 'List Topics',
description: 'Retrieve all topics from your Hedy workspace.',
props: {
returnAll: commonProps.returnAll,
limit: commonProps.limit,
},
async run(context) {
const client = new HedyApiClient(context.auth.secret_text);
const { returnAll, limit } = context.propsValue as {
returnAll?: boolean;
limit?: number;
};
const response = await client.request<Topic[]>({
method: HttpMethod.GET,
path: '/topics',
});
const topics = toTopicArray(response);
if (!returnAll) {
const limited = assertLimit(limit);
return limited ? topics.slice(0, limited) : topics.slice(0, 50);
}
return topics;
},
});

View File

@@ -0,0 +1,41 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { PieceAuth } from '@activepieces/pieces-framework';
import { HedyApiClient } from '../common/client';
export const hedyAuth = PieceAuth.SecretText({
displayName: 'API Key',
description:
'Generate an API key from your Hedy dashboard under Settings → API, then paste the key here (it begins with `hedy_live_`).',
required: true,
validate: async ({ auth }) => {
if (!auth || typeof auth !== 'string') {
return {
valid: false,
error: 'Please provide a valid API key.',
};
}
const client = new HedyApiClient(auth);
try {
await client.request({
method: HttpMethod.GET,
path: '/sessions',
queryParams: {
limit: 1,
},
});
return {
valid: true,
};
} catch (error) {
return {
valid: false,
error:
error instanceof Error
? error.message
: 'Invalid API key. Please verify the key in your Hedy dashboard and try again.',
};
}
},
});

View File

@@ -0,0 +1,203 @@
import {
AuthenticationType,
httpClient,
HttpMethod,
HttpRequest,
QueryParams,
} from '@activepieces/pieces-common';
import { HedyApiError } from './errors';
import {
ApiErrorPayload,
HedyResponse,
PaginatedResponse,
PaginationInfo,
} from './types';
const BASE_URL = 'https://api.hedy.bot';
const DEFAULT_LIMIT = 50;
const MAX_RESULTS = 1000;
const MAX_RETRIES = 3;
const INITIAL_BACKOFF_MS = 500;
export interface PaginationOptions {
returnAll?: boolean;
limit?: number;
after?: string;
before?: string;
topicId?: string;
format?: 'standard' | 'zapier';
}
interface RequestOptions {
method: HttpMethod;
path: string;
body?: unknown;
queryParams?: Record<string, unknown>;
}
export class HedyApiClient {
constructor(private readonly apiKey: string) {}
async request<T>(options: RequestOptions): Promise<HedyResponse<T>> {
return this.withRetry(() => this.performRequest<T>(options));
}
async paginate<T>(path: string, options: PaginationOptions = {}): Promise<T[]> {
const { returnAll = false, limit = DEFAULT_LIMIT, ...rest } = options;
const collected: T[] = [];
let cursor = rest.after;
let hasMore = true;
while (hasMore) {
const query = {
...rest,
limit: returnAll ? Math.min(limit, 100) : limit,
after: cursor,
};
const response = await this.request<T>({
method: HttpMethod.GET,
path,
queryParams: query,
});
const { data, pagination } = normalizeListResult(response);
collected.push(...data);
if (!returnAll && collected.length >= limit) {
// When NOT returning all, stop when we hit the limit
hasMore = false;
} else if (pagination?.hasMore && pagination.next) {
// Continue if there's more data available
cursor = pagination.next;
hasMore = true;
} else {
// No more data available
hasMore = false;
}
if (collected.length >= MAX_RESULTS) {
hasMore = false;
}
}
return returnAll ? collected : collected.slice(0, limit);
}
private async performRequest<T>({ method, path, body, queryParams }: RequestOptions): Promise<HedyResponse<T>> {
const qs: QueryParams = {};
if (queryParams) {
for (const [key, value] of Object.entries(queryParams)) {
if (value === undefined || value === null || value === '') {
continue;
}
qs[key] = String(value);
}
}
const request: HttpRequest = {
method,
url: `${BASE_URL}${path}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: this.apiKey,
},
headers: {
'User-Agent': 'activepieces-hedy/1.0.0',
'Content-Type': 'application/json',
},
body,
queryParams: qs,
};
try {
const response = await httpClient.sendRequest<HedyResponse<T>>(request);
const { body: responseBody } = response;
if (isErrorPayload(responseBody)) {
throw HedyApiError.fromPayload(responseBody, undefined, response.status);
}
return responseBody;
} catch (error: any) {
const apiErrorPayload: ApiErrorPayload | undefined = error?.response?.body;
const statusCode: number | undefined = error?.response?.status;
throw HedyApiError.fromPayload(apiErrorPayload, error, statusCode);
}
}
private async withRetry<T>(operation: () => Promise<T>): Promise<T> {
let attempt = 0;
let backoff = INITIAL_BACKOFF_MS;
while (attempt < MAX_RETRIES) {
try {
return await operation();
} catch (error) {
const isLastAttempt = attempt === MAX_RETRIES - 1;
if (!(error instanceof HedyApiError) || !this.shouldRetry(error) || isLastAttempt) {
throw error;
}
await this.delay(backoff);
backoff *= 2;
attempt += 1;
}
}
// The loop above either returns or throws, but TypeScript expects a return.
throw new HedyApiError('unknown_error', 'Request failed after multiple retries.');
}
private shouldRetry(error: HedyApiError): boolean {
return error.code === 'rate_limit_exceeded';
}
private async delay(duration: number): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, duration));
}
}
function isErrorPayload<T>(body: HedyResponse<T>): body is ApiErrorPayload {
return Boolean(body && typeof body === 'object' && 'error' in body);
}
function normalizeListResult<T>(result: HedyResponse<T>): {
data: T[];
pagination?: PaginationInfo;
} {
if (Array.isArray(result)) {
return { data: result };
}
if (result && typeof result === 'object') {
const maybePaginated = result as PaginatedResponse<T>;
if (Array.isArray(maybePaginated.data)) {
return {
data: maybePaginated.data,
pagination: maybePaginated.pagination,
};
}
const maybeData = (result as { data?: unknown }).data;
if (Array.isArray(maybeData)) {
return { data: maybeData as T[] };
}
}
return {
data: result ? [result as T] : [],
};
}
export function unwrapResource<T>(result: HedyResponse<T>): T {
if (result && typeof result === 'object') {
const data = (result as { data?: unknown }).data;
if (data && !Array.isArray(data)) {
return data as T;
}
}
return result as T;
}

View File

@@ -0,0 +1,78 @@
import { ApiErrorPayload } from './types';
const DEFAULT_ERROR_MESSAGE = 'An unknown error occurred while communicating with Hedy.';
function deriveCode(initialCode: string | undefined, statusCode?: number): string {
if (initialCode) {
return initialCode;
}
if (statusCode === 429) {
return 'rate_limit_exceeded';
}
return 'unknown_error';
}
const FRIENDLY_MESSAGES: Record<string, string> = {
webhook_limit_exceeded:
'Maximum webhook limit (10) reached. Please delete unused webhooks in your Hedy dashboard.',
authentication_failed:
'Invalid API key. Please check your Hedy dashboard for the correct API key.',
invalid_event:
'Invalid event type. Valid events: session.created, session.ended, highlight.created, todo.exported.',
invalid_webhook_url:
'Webhook URL must be publicly accessible. For local testing, use a tunneling service like ngrok.',
invalid_parameter:
'Invalid request parameter. Please review your configuration and try again.',
rate_limit_exceeded:
'Rate limit reached. Please wait a moment before trying again.',
unknown_error: DEFAULT_ERROR_MESSAGE,
};
export class HedyApiError extends Error {
constructor(
public readonly code: string,
message: string,
public readonly details?: unknown,
) {
super(message || DEFAULT_ERROR_MESSAGE);
this.name = 'HedyApiError';
}
static fromPayload(
payload: ApiErrorPayload | undefined,
fallback?: unknown,
statusCode?: number,
): HedyApiError {
if (payload && payload.error) {
const { code, message } = payload.error;
const resolvedCode = deriveCode(code, statusCode);
return new HedyApiError(
resolvedCode,
FRIENDLY_MESSAGES[resolvedCode] ?? message ?? DEFAULT_ERROR_MESSAGE,
payload,
);
}
if (fallback instanceof HedyApiError) {
return fallback;
}
if (fallback instanceof Error) {
const derivedCode = deriveCode(undefined, statusCode);
return new HedyApiError(
derivedCode,
FRIENDLY_MESSAGES[derivedCode] ?? fallback.message ?? DEFAULT_ERROR_MESSAGE,
fallback,
);
}
const finalCode = deriveCode(undefined, statusCode);
return new HedyApiError(
finalCode,
FRIENDLY_MESSAGES[finalCode] ?? DEFAULT_ERROR_MESSAGE,
fallback,
);
}
}

View File

@@ -0,0 +1,70 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { Property } from '@activepieces/pieces-framework';
import { HedyApiClient } from './client';
import { PaginatedResponse, Topic } from './types';
import { hedyAuth } from '../auth';
function toTopicArray(result: unknown): Topic[] {
if (Array.isArray(result)) {
return result as Topic[];
}
if (result && typeof result === 'object' && 'data' in result) {
const data = (result as PaginatedResponse<Topic>).data;
if (Array.isArray(data)) {
return data;
}
}
return [];
}
export const topicDropdown = Property.Dropdown({
auth: hedyAuth,
displayName: 'Topic',
description: 'Optionally filter results by a specific topic.',
required: false,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Connect your Hedy account first.',
};
}
const client = new HedyApiClient(auth.secret_text);
try {
const response = await client.request<Topic[]>({
method: HttpMethod.GET,
path: '/topics',
});
const topics = toTopicArray(response);
if (topics.length === 0) {
return {
disabled: false,
options: [],
placeholder: 'No topics found in your Hedy workspace.',
};
}
return {
disabled: false,
options: topics.map((topic) => ({
label: topic.name,
value: topic.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder:
error instanceof Error ? error.message : 'Failed to load topics. Check your connection.',
};
}
},
});

View File

@@ -0,0 +1,60 @@
import { Property } from '@activepieces/pieces-framework';
export const commonProps = {
sessionId: Property.ShortText({
displayName: 'Session ID',
description: 'The session ID as shown in the Hedy dashboard.',
required: true,
}),
highlightId: Property.ShortText({
displayName: 'Highlight ID',
description: 'The highlight ID as shown in the Hedy dashboard.',
required: true,
}),
topicId: Property.ShortText({
displayName: 'Topic ID',
description: 'The topic ID as shown in the Hedy dashboard.',
required: true,
}),
returnAll: Property.Checkbox({
displayName: 'Return All',
description: 'Return all results instead of using the limit.',
required: false,
defaultValue: false,
}),
limit: Property.Number({
displayName: 'Limit',
description: 'Maximum number of results to return (default 50).',
required: false,
defaultValue: 50,
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Select the response format to use.',
required: false,
defaultValue: 'standard',
options: {
options: [
{ label: 'Standard', value: 'standard' },
{ label: 'Zapier Compatible', value: 'zapier' },
],
},
}),
afterCursor: Property.ShortText({
displayName: 'After Cursor',
description: 'Pagination cursor used to fetch results after a specific item.',
required: false,
}),
beforeCursor: Property.ShortText({
displayName: 'Before Cursor',
description: 'Pagination cursor used to fetch results before a specific item.',
required: false,
}),
};

View File

@@ -0,0 +1,103 @@
export interface Topic {
id: string;
name: string;
color?: string;
iconName?: string;
}
export interface Todo {
id: string;
text: string;
dueDate?: string;
completed: boolean;
topic?: Topic;
}
export interface Conversation {
question: string;
answer: string;
timestamp?: string;
}
export interface Session {
id: string;
title: string;
startTime: string;
endTime?: string;
duration?: number;
transcript?: string;
conversations?: Conversation[] | string;
meeting_minutes?: string;
meetingMinutes?: string;
recap?: string;
user_todos?: Todo[];
userTodos?: Todo[];
topic?: Topic;
}
export interface Highlight {
id: string;
sessionId: string;
timestamp?: string;
title?: string;
rawQuote?: string;
cleanedQuote?: string;
mainIdea?: string;
aiInsights?: string;
}
export interface TodoExportedPayload {
id: string;
sessionId: string;
text: string;
dueDate?: string;
}
export interface PaginationInfo {
hasMore: boolean;
next?: string;
previous?: string;
}
export interface PaginatedResponse<T> {
success?: boolean;
data: T[];
pagination?: PaginationInfo;
}
export interface ApiSuccessResponse<T> {
success: true;
data: T;
}
export interface ApiErrorPayload {
success?: false;
error: {
code: string;
message: string;
};
}
export type HedyResponse<T> =
| T
| T[]
| PaginatedResponse<T>
| ApiSuccessResponse<T>
| ApiErrorPayload;
export interface WebhookRegistration {
id?: string;
url: string;
events: string[];
signingSecret?: string;
createdAt?: string;
updatedAt?: string;
enabled?: boolean;
}
export enum HedyWebhookEvent {
SessionCreated = 'session.created',
SessionEnded = 'session.ended',
HighlightCreated = 'highlight.created',
TodoExported = 'todo.exported',
}

View File

@@ -0,0 +1,15 @@
export function assertLimit(limit?: number): number | undefined {
if (limit === undefined || limit === null) {
return limit ?? undefined;
}
if (Number.isNaN(limit)) {
throw new Error('Limit must be a number between 1 and 100.');
}
if (limit < 1 || limit > 100) {
throw new Error('Limit must be between 1 and 100.');
}
return limit;
}

View File

@@ -0,0 +1 @@
export * from './webhook';

View File

@@ -0,0 +1,157 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { Property, TriggerStrategy, createTrigger } from '@activepieces/pieces-framework';
import { createHmac, timingSafeEqual } from 'crypto';
import { hedyAuth } from '../../auth';
import { HedyApiClient, unwrapResource } from '../../common/client';
import { HedyWebhookEvent, WebhookRegistration } from '../../common/types';
interface TriggerConfig {
event: HedyWebhookEvent;
name: string;
displayName: string;
description: string;
sampleData?: unknown;
}
export function createHedyWebhookTrigger(config: TriggerConfig) {
return createTrigger({
auth: hedyAuth,
name: config.name,
displayName: config.displayName,
description: config.description,
type: TriggerStrategy.WEBHOOK,
props: {
verifySignature: Property.Checkbox({
displayName: 'Verify Signature',
description:
'Verify the webhook signature using the secret returned by Hedy. Disable this option if Hedy does not provide a signing secret for your account.',
required: false,
defaultValue: false,
}),
},
sampleData: config.sampleData,
async onEnable(context) {
const client = new HedyApiClient(context.auth.secret_text);
const webhookUrl = context.webhookUrl;
if (!webhookUrl) {
throw new Error('Webhook URL is unavailable. Please try again.');
}
const response = await client.request<WebhookRegistration>({
method: HttpMethod.POST,
path: '/webhooks',
body: {
url: webhookUrl,
events: [config.event],
},
});
const webhook = unwrapResource<WebhookRegistration>(response);
if (!webhook?.id) {
throw new Error('Failed to register webhook with Hedy. No webhook ID was returned.');
}
await context.store.put('webhookId', webhook.id);
if (webhook.signingSecret) {
await context.store.put('signingSecret', webhook.signingSecret);
} else {
await context.store.delete('signingSecret');
}
},
async onDisable(context) {
const webhookId = (await context.store.get<string>('webhookId')) ?? undefined;
if (!webhookId) {
return;
}
const client = new HedyApiClient(context.auth.secret_text);
try {
await client.request({
method: HttpMethod.DELETE,
path: `/webhooks/${webhookId}`,
});
} catch (error) {
// Ignore deletion errors webhook may already be removed.
} finally {
await context.store.delete('webhookId');
await context.store.delete('signingSecret');
}
},
async run(context) {
const props = context.propsValue as Record<string, unknown>;
const verifySignatureEnabled = Boolean(props['verifySignature']);
const payload = (context.payload.body ?? {}) as Record<string, unknown>;
if (verifySignatureEnabled) {
const signatureHeader = getSignatureHeader(context.payload.headers ?? {});
if (!signatureHeader) {
throw new Error('Hedy signature header is missing from the webhook request.');
}
const signingSecret = await context.store.get<string>('signingSecret');
if (!signingSecret) {
throw new Error(
'Hedy did not return a signing secret during webhook registration. Disable signature verification or re-register your webhook.',
);
}
const rawBody = extractRawBody(context.payload.rawBody as RawBody, payload);
const expectedSignature = createHmac('sha256', signingSecret).update(rawBody).digest('hex');
if (!secureCompare(expectedSignature, signatureHeader)) {
throw new Error('Webhook signature verification failed. This request may not be from Hedy.');
}
}
const eventType = payload['event'] as string | undefined;
if (eventType && eventType !== config.event) {
return [];
}
return [payload];
},
});
}
type HeadersMap = Record<string, string | string[] | undefined>;
type RawBody = string | Buffer | undefined;
function getSignatureHeader(headers: HeadersMap): string | undefined {
const normalized: Record<string, string> = {};
for (const [key, value] of Object.entries(headers)) {
if (typeof value === 'string') {
normalized[key.toLowerCase()] = value;
} else if (Array.isArray(value) && value.length > 0) {
normalized[key.toLowerCase()] = value[0];
}
}
return normalized['x-hedy-signature'];
}
function extractRawBody(rawBody: RawBody, payload: Record<string, unknown>): Buffer {
if (typeof rawBody === 'string') {
return Buffer.from(rawBody, 'utf8');
}
if (rawBody instanceof Buffer) {
return rawBody;
}
return Buffer.from(JSON.stringify(payload ?? {}), 'utf8');
}
function secureCompare(expected: string, candidate: string): boolean {
const expectedBuffer = Buffer.from(expected, 'hex');
const candidateBuffer = Buffer.from(candidate, 'hex');
if (expectedBuffer.length !== candidateBuffer.length) {
return false;
}
return timingSafeEqual(expectedBuffer, candidateBuffer);
}

View File

@@ -0,0 +1,9 @@
import { HedyWebhookEvent } from '../../common/types';
import { createHedyWebhookTrigger } from './factory';
export const highlightCreated = createHedyWebhookTrigger({
event: HedyWebhookEvent.HighlightCreated,
name: 'highlight-created',
displayName: 'Highlight Created',
description: 'Triggers when a highlight is created during a session.',
});

View File

@@ -0,0 +1,4 @@
export * from './session-created';
export * from './session-ended';
export * from './highlight-created';
export * from './todo-exported';

View File

@@ -0,0 +1,9 @@
import { HedyWebhookEvent } from '../../common/types';
import { createHedyWebhookTrigger } from './factory';
export const sessionCreated = createHedyWebhookTrigger({
event: HedyWebhookEvent.SessionCreated,
name: 'session-created',
displayName: 'Session Created',
description: 'Triggers when a new session is created in Hedy.',
});

View File

@@ -0,0 +1,9 @@
import { HedyWebhookEvent } from '../../common/types';
import { createHedyWebhookTrigger } from './factory';
export const sessionEnded = createHedyWebhookTrigger({
event: HedyWebhookEvent.SessionEnded,
name: 'session-ended',
displayName: 'Session Ended',
description: 'Triggers when a session is completed in Hedy.',
});

View File

@@ -0,0 +1,9 @@
import { HedyWebhookEvent } from '../../common/types';
import { createHedyWebhookTrigger } from './factory';
export const todoExported = createHedyWebhookTrigger({
event: HedyWebhookEvent.TodoExported,
name: 'todo-exported',
displayName: 'Todo Exported',
description: 'Triggers when a todo item is exported from Hedy.',
});

View File

@@ -0,0 +1,20 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"importHelpers": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}