granola-webhooks-events

Handle Granola webhook events and build event-driven automations. Use when building custom integrations, processing meeting events, or creating real-time notification systems. Trigger with phrases like "granola webhooks", "granola events", "granola triggers", "granola real-time", "granola callbacks". allowed-tools: Read, Write, Edit, Bash(curl:*) version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>

Allowed Tools

No tools specified

Provided by Plugin

granola-pack

Claude Code skill pack for Granola AI meeting notes (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the granola-pack plugin:

/plugin install granola-pack@claude-code-plugins-plus

Click to copy

Instructions

# Granola Webhooks & Events ## Overview Build event-driven automations using Granola's Zapier webhooks and event triggers. ## Prerequisites - Granola Pro or Business plan - Zapier account - Webhook endpoint (or Zapier as processor) - Understanding of event-driven architecture ## Available Events ### Granola Zapier Triggers | Event | Description | Payload | |-------|-------------|---------| | New Note Created | Meeting ended, notes ready | Full note data | | Note Updated | Notes manually edited | Updated content | | Note Shared | Notes shared with others | Share details | ## Event Payloads ### New Note Created ```json { "event_type": "note.created", "timestamp": "2025-01-06T14:30:00Z", "data": { "note_id": "note_abc123", "meeting_title": "Sprint Planning", "meeting_date": "2025-01-06", "start_time": "2025-01-06T14:00:00Z", "end_time": "2025-01-06T14:30:00Z", "duration_minutes": 30, "attendees": [ { "name": "Sarah Chen", "email": "sarah@company.com" } ], "summary": "Discussed Q1 priorities...", "action_items": [ { "text": "Review PRs", "assignee": "@mike", "due_date": "2025-01-08" } ], "key_points": [ "Agreed on feature freeze date", "Sprint velocity improving" ], "transcript_available": true, "granola_url": "https://app.granola.ai/notes/note_abc123" } } ``` ### Note Updated ```json { "event_type": "note.updated", "timestamp": "2025-01-06T15:00:00Z", "data": { "note_id": "note_abc123", "changes": { "summary": { "old": "Discussed Q1 priorities...", "new": "Finalized Q1 priorities..." }, "action_items": { "added": [{"text": "New action", "assignee": "@alex"}], "removed": [] } }, "updated_by": "user@company.com" } } ``` ## Webhook Processing ### Zapier Webhook Receiver ```yaml # Create Catch Hook in Zapier Trigger: Webhooks by Zapier Event: Catch Hook URL: https://hooks.zapier.com/hooks/catch/YOUR_HOOK_ID/ # Configure in Granola (via Zapier integration) Granola → Zapier → Your Webhook ``` ### Custom Webhook Endpoint ```javascript // Express.js webhook handler const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhook/granola', (req, res) => { const event = req.body; console.log(`Received event: ${event.event_type}`); switch (event.event_type) { case 'note.created': handleNewNote(event.data); break; case 'note.updated': handleNoteUpdate(event.data); break; default: console.log('Unknown event type'); } res.status(200).json({ received: true }); }); async function handleNewNote(data) { // Process new meeting notes console.log(`New note: ${data.meeting_title}`); // Extract action items for (const action of data.action_items) { await createTask(action); } // Send notification await notifyTeam(data); } app.listen(3000); ``` ### Python Webhook Handler ```python from flask import Flask, request, jsonify import json app = Flask(__name__) @app.route('/webhook/granola', methods=['POST']) def granola_webhook(): event = request.json event_type = event.get('event_type') data = event.get('data') if event_type == 'note.created': process_new_note(data) elif event_type == 'note.updated': process_note_update(data) return jsonify({'status': 'ok'}), 200 def process_new_note(data): print(f"Processing: {data['meeting_title']}") # Create issues for action items for action in data.get('action_items', []): create_github_issue(action) # Post to Slack post_to_slack(data) if __name__ == '__main__': app.run(port=3000) ``` ## Event Filtering ### Zapier Filters ```yaml # Filter by meeting type Filter Step: Condition: meeting_title contains "sprint" OR meeting_title contains "planning" OR attendees count > 3 Action: Continue # Filter by content Filter Step: Condition: summary contains "decision" OR action_items exists Action: Continue ``` ### Code-Based Filtering ```javascript // Zapier Code Step const data = inputData; // Only process if has action items if (!data.action_items || data.action_items.length === 0) { return { skip: true }; } // Only process external meetings const externalDomains = ['client.com', 'partner.org']; const hasExternal = data.attendees.some(a => externalDomains.some(d => a.email.includes(d)) ); if (!hasExternal) { return { skip: true }; } return { process: true, ...data }; ``` ## Real-Time Processing Patterns ### Pattern 1: Immediate Notification ```yaml Event Flow: Meeting Ends (T+0) ↓ Notes Ready (T+2 min) ↓ Webhook Fires (T+2.1 min) ↓ Slack Notification (T+2.2 min) Total Latency: ~2-3 minutes ``` ### Pattern 2: Batch Processing ```yaml Event Flow: Notes Created → Queue ↓ Every 15 minutes: - Aggregate notes - Generate digest - Send single notification Use Case: Reduce notification noise ``` ### Pattern 3: Conditional Routing ```yaml Event Received: │ ├── If external attendee → CRM Update │ ├── If action items > 3 → Create Project │ ├── If duration > 60 min → Request Summary │ └── Default → Standard Processing ``` ## Error Handling ### Retry Logic ```javascript async function processWithRetry(data, maxRetries = 3) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { await processEvent(data); return { success: true }; } catch (error) { console.error(`Attempt ${attempt} failed:`, error); if (attempt === maxRetries) { await notifyError(data, error); return { success: false, error }; } // Exponential backoff await sleep(Math.pow(2, attempt) * 1000); } } } ``` ### Dead Letter Queue ```yaml On Error: 1. Log error details 2. Store failed event in queue 3. Alert ops team 4. Retry after 1 hour 5. If still failing, archive for manual review ``` ## Monitoring & Observability ### Event Logging ```javascript // Log all events for debugging function logEvent(event) { const log = { timestamp: new Date().toISOString(), event_type: event.event_type, note_id: event.data.note_id, meeting_title: event.data.meeting_title, processing_time: Date.now() }; console.log(JSON.stringify(log)); } ``` ### Metrics to Track | Metric | Description | Alert Threshold | |--------|-------------|-----------------| | Events/hour | Processing volume | > 100/hr | | Latency | Time to process | > 30 seconds | | Error rate | Failed events | > 5% | | Queue depth | Pending events | > 50 | ## Resources - [Zapier Webhooks](https://zapier.com/help/create/code-webhooks) - [Webhook Best Practices](https://zapier.com/blog/webhook-best-practices) ## Next Steps Proceed to `granola-performance-tuning` for optimization techniques.

Skill file: plugins/saas-packs/granola-pack/skills/granola-webhooks-events/SKILL.md