Agent skill

Typefully Webhooks Skill

Receive and verify Typefully webhooks. Use when setting up Typefully API v2 webhook handlers, debugging X-Typefully-Signature verification, or handling draft events like draft.created, draft.scheduled, draft.published, draft.status_changed, and draft.deleted.

Install this skill

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


When to Use This Skill

  • How do I receive Typefully webhooks?
  • How do I verify the X-Typefully-Signature header (sha256=<hex>)?
  • How do I handle draft.published or draft.scheduled events?
  • Why is my Typefully webhook signature verification failing?
  • Which events does Typefully send, and how does it retry?

Essential Code (USE THIS)

Typefully (API v2) sends a JSON envelope { "event": "draft.created", "data": { ...draft } } and signs each delivery with three headers:

X-Typefully-Event: draft.published
X-Typefully-Timestamp: 1737381605            (Unix seconds)
X-Typefully-Signature: sha256=<64 hex chars>
  • Algorithm: HMAC-SHA256, lowercase hex, keyed with the webhook secret (from your Typefully API settings) used as-is, as UTF-8.
  • Signed message: the X-Typefully-Timestamp value, a literal ., then the raw request body bytes.
  • Typefully's docs only say to check that the timestamp is recent; they don't give a window. The examples leave it off by default (see below).

Verification (Node.js)

const crypto = require('crypto');

// rawBody: Buffer of the exact request bytes (express.raw({ type: '*/*' }), not express.json)
function verifyTypefully(rawBody, timestamp, signature, secret) {
  if (!secret || !timestamp || !signature) return false;
  const digest = signature.startsWith('sha256=') ? signature.slice(7) : signature;
  if (!/^[0-9a-fA-F]{64}$/.test(digest)) return false; // validate before decoding
  const expected = crypto.createHmac('sha256', secret)
    .update(Buffer.concat([Buffer.from(timestamp, 'latin1'), Buffer.from('.'), rawBody]))
    .digest();
  const received = Buffer.from(digest.toLowerCase(), 'hex');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

Verification (Python)

import hashlib, hmac, re

def verify_typefully(raw_body: bytes, timestamp, signature, secret: str) -> bool:
    if not secret or not timestamp or not signature:
        return False
    digest = signature[len("sha256="):] if signature.startswith("sha256=") else signature
    if not re.fullmatch(r"[0-9a-fA-F]{64}", digest):
        return False
    message = b"".join([timestamp.encode("latin-1"), b".", raw_body])
    expected = hmac.new(secret.encode(), message, hashlib.sha256).digest()
    return hmac.compare_digest(bytes.fromhex(digest), expected)

Respond 401 when the signature or timestamp header is missing, malformed, or doesn't match. Respond with any 2xx once the event is accepted.

Replay window (opt-in): Typefully suggests checking that the timestamp is recent. The examples don't, by default: Hookdeck's retries and replays resend the original timestamp, and Typefully doesn't document whether its own retries do, so a fixed window could reject legitimate redeliveries. Set TYPEFULLY_WEBHOOK_TOLERANCE_SECONDS to a positive number of seconds (the timestamp is Unix seconds) to opt in; unset or 0 leaves it off. Use idempotency on data.id plus the event for duplicate protection.

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

Common Event Types

The type is in the body's event field (and the X-Typefully-Event header). The docs list seven:

EventDescription
draft.createdA new draft is created
draft.plannedA draft is planned (dated but inert until confirmed)
draft.scheduledA draft is scheduled for publishing
draft.publishedA draft is successfully published
draft.status_changedAny status transition (includes planned, scheduled, published)
draft.tags_changedA draft's tags are modified
draft.deletedA draft is deleted

data holds the full draft (id, social_set_id, status, scheduled_date, tags, per-platform platforms, published URLs such as x_published_url). See references/overview.md.

For the full event reference, see Typefully's API docs.

Important Headers

HeaderDescription
X-Typefully-Signaturesha256=<hex> HMAC-SHA256 signature
X-Typefully-TimestampUnix timestamp (seconds) when the event was generated
X-Typefully-EventThe event type, for example draft.published

Environment Variables

TYPEFULLY_WEBHOOK_SECRET=your_typefully_webhook_secret   # from Typefully's API settings
TYPEFULLY_WEBHOOK_TOLERANCE_SECONDS=0   # optional; a positive value opts in to the replay window

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 11, 2026

View on GitHub →