Agent skill

Fathom Webhooks Skill

Receive and verify Fathom (fathom.video / fathom.ai) AI meeting notetaker webhooks. Use when setting up a Fathom webhook handler, debugging webhook-signature verification (Standard Webhooks, whsec_ secret), or processing the "new meeting content ready" payload with the meeting summary, transcript, and action items. Not for Fathom Analytics (usefathom.com).

Install this skill

npx skills add hookdeck/webhook-skills --skill fathom-webhooks


Fathom is an AI meeting notetaker. After a recorded meeting is processed, Fathom POSTs the meeting (optionally with its summary, transcript, action items and CRM matches) to your webhook URL. This skill covers Fathom the notetaker (fathom.video / developers.fathom.ai), not Fathom Analytics (usefathom.com).

When to Use This Skill

  • How do I receive Fathom webhooks after a meeting is recorded?
  • How do I verify Fathom webhook signatures (webhook-signature)?
  • How do I get the Fathom meeting summary, transcript, or action items via webhook?
  • Why is my Fathom webhook signature verification failing?
  • Why isn't my Fathom webhook firing?

Verification (core)

Fathom uses Standard Webhooks. Every request carries webhook-id, webhook-timestamp (Unix seconds) and webhook-signature (space-delimited v1,<base64> list). The signature is base64(HMAC-SHA256) over {webhook-id}.{webhook-timestamp}.{raw body}, keyed with the base64-decoded part of your whsec_... secret.

The official SDKs wrap this in one call. It throws on a bad signature or a timestamp more than 5 minutes off, and returns the parsed payload:

import { Fathom } from 'fathom-typescript';   // Python: from fathom_python import Fathom
const meeting = Fathom.verifyWebhook(process.env.FATHOM_WEBHOOK_SECRET, headers, rawBody);

Manual equivalent (Node), if you can't use the SDK:

const crypto = require('crypto');

function verifyFathomWebhook(secret, id, timestamp, signatureHeader, rawBody) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const key = Buffer.from(secret.split('_')[1], 'base64'); // decode part after whsec_
  const expected = crypto.createHmac('sha256', key)
    .update(`${id}.${timestamp}.${rawBody}`).digest('base64');
  return signatureHeader.split(' ').some((entry) => {
    const sig = entry.split(',')[1] ?? entry;               // strip "v1,"
    try { return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); }
    catch { return false; }                                 // length mismatch
  });
}

Use the raw body (before JSON parsing). Re-serialized JSON will not match.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Webhook Payload

Fathom sends one webhook type, new meeting content ready, fired once per meeting when the recording finalizes (the same moment the summary email goes out). The body is the Meeting object. There is no event-type field to dispatch on.

FieldNotes
title, meeting_titleRecording title; calendar event title (nullable)
recording_idInteger ID of the recording
url, share_urlLinks to the recording in Fathom
created_at, recording_start_time, recording_end_timeISO 8601
recorded_by{ name, email, email_domain, team }
calendar_invitees[{ name, email, email_domain, is_external }]
shared_withno_teams, single_team, multiple_teams, all_teams
default_summary{ template_name, markdown_formatted } if include_summary
transcript[{ speaker: { display_name }, text, timestamp }] if include_transcript
action_items[{ description, completed, assignee, ... }] if include_action_items
crm_matches{ contacts, companies, deals } if include_crm_matches

Important Headers

HeaderDescription
webhook-idUnique message ID; unchanged on retries, so use it for idempotency
webhook-timestampUnix timestamp in seconds
webhook-signaturev1,<base64> entries, space-delimited

Environment Variables

FATHOM_WEBHOOK_SECRET=whsec_xxxxx   # One secret per webhook, shown when it is created

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 fathom --path /webhooks/fathom

Set the printed URL as the webhook's Destination URL in Fathom (Settings → API Access → Manage → Add Webhook), then use Send test payload or record a short meeting.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →