Agent skill

Circleback Webhooks Skill

Receive and verify Circleback (circleback.ai AI meeting notes) automation webhooks. Use when setting up a Circleback webhook handler, debugging x-signature verification (HMAC-SHA256 hex of the raw body), or processing the meeting export payload with notes, action items, attendees, transcript and insights.

Install this skill

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


Circleback is an AI meeting notetaker. A Send webhook request step in a Circleback automation POSTs each matching meeting (notes, action items, attendees, and optionally transcript and insights) as JSON to your endpoint.

When to Use This Skill

  • How do I receive Circleback webhooks after a meeting?
  • How do I verify the Circleback x-signature header?
  • How do I get Circleback meeting notes, action items or transcript via webhook?
  • Why is my Circleback webhook signature verification failing?
  • How do I export past Circleback meetings to my own endpoint?

Verification (core)

Circleback sends x-signature: the bare lowercase hex HMAC-SHA256 of the raw request body, keyed with the signing secret shown in the automation's webhook step (used as a UTF-8 string). There is no timestamp and no prefix. Check the header is exactly 64 hex characters before decoding (Buffer.from(x, 'hex') silently truncates invalid hex), then compare in constant time:

const crypto = require('crypto');

function verifyCirclebackSignature(rawBody, signature, secret) {
  if (typeof signature !== 'string' || !/^[0-9a-f]{64}$/i.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}

Python (hmac.compare_digest on bytes, so a non-ASCII header gives 401, not 500):

import hashlib, hmac, re

def verify_circleback_signature(raw_body: bytes, signature: str | None, secret: str) -> bool:
    if not signature or not re.fullmatch(r"[0-9a-fA-F]{64}", signature):
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest().encode()
    return hmac.compare_digest(expected, signature.lower().encode())

Read the raw body (express.raw({ type: '*/*' }), request.text(), await request.body()), not re-serialized JSON. Respond 401 on a bad signature.

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

Webhook Payload

There are no event types and no event-name field: every delivery is one meeting export, sent when an automation matches a meeting (or when you run the automation on past meetings). The fields included follow the automation's settings.

FieldNotes
idCircleback meeting ID; stable, so use it for idempotency. Link: https://circleback.ai/meetings/${id}
name, createdAt, durationMeeting name, ISO timestamp, seconds
url, recordingUrlVirtual meeting URL; recording URL (valid 24 hours, only if recordings are saved), both nullable
tags, icalUidTag names; calendar event UID (nullable)
attendees[{ name, email }] (both nullable)
notesMarkdown meeting notes
actionItems[{ id, title, description, assignee: { name, email } | null, status: 'PENDING' | 'DONE' }]
transcript[{ speaker, text, timestamp }] (seconds)
insights{ [insightName]: [{ insight, speaker, timestamp }] }

Important Headers

HeaderDescription
x-signatureHex HMAC-SHA256 of the raw body (optional on Circleback's side, recommended)

Environment Variables

CIRCLEBACK_SIGNING_SECRET=your_signing_secret   # Shown in the automation's webhook step

Local Development

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

Use the printed URL as the endpoint in the automation's Send webhook request step, then send a test request for the most recent meeting.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 6, 2026

View on GitHub →