Agent skill

Zernio Webhooks Skill

Receive and verify Zernio webhooks (formerly Late, getlate.dev). Use when setting up Zernio webhook handlers, debugging X-Zernio-Signature verification, or handling social media events like post.published, post.failed, message.received, or account.disconnected.

Install this skill

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


When to Use This Skill

  • How do I receive Zernio webhooks?
  • How do I verify the X-Zernio-Signature header?
  • How do I handle post.published, post.failed or message.received events?
  • Why is my Zernio webhook signature verification failing?
  • Why am I receiving the same Zernio event twice?

Verification (core)

Zernio signs a delivery only when the endpoint has a secret, and you supply that secret when you create the endpoint (Zernio never generates one). X-Zernio-Signature is the lowercase hex HMAC-SHA256 of the raw request body keyed with that secret. X-Late-Signature is a legacy alias with the same value. There is no timestamp in the signed content.

Node:

const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
  if (!signatureHeader || !secret) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}
// header: req.headers['x-zernio-signature'] || req.headers['x-late-signature']

Python:

import hmac, hashlib

def verify(raw_body: bytes, signature_header: str | None, secret: str) -> bool:
    if not signature_header or not secret:
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature_header, expected)

Zernio has no SDK helper for verification (the docs show the manual HMAC), so all three examples verify manually. Always set a secret: an endpoint without one receives unsigned deliveries.

For complete handlers with route wiring, event dispatch, and tests, see:

Common Event Types

EventDescription
post.scheduledA post entered the scheduled state
post.publishedThe post published on every target platform
post.partialPublished on some platforms, failed on others
post.failedThe post failed on every target platform
post.platform.publishedOne platform target finished publishing
post.platform.failedOne platform target failed permanently
message.receivedA new inbox message (DM) arrived
comment.receivedA new comment arrived on a tracked post
account.connectedAn account was connected to a profile
account.disconnectedA connected account stopped working or was removed
webhook.testSent by POST /v1/webhooks/test regardless of subscribed events

For the full event list (ads, calls, WhatsApp, automations and more), see Zernio webhooks and references/overview.md.

Important Headers

HeaderDescription
X-Zernio-SignatureLowercase hex HMAC-SHA256 of the raw body (only when the endpoint has a secret)
X-Late-SignatureLegacy alias, same value
X-Zernio-EventEvent name, e.g. post.published
X-Zernio-Event-IdStable event UUID, identical on retries; use as the dedupe key
X-Late-Event-IdLegacy alias of X-Zernio-Event-Id

Delivery Behavior

  • Return any 2xx within 5 seconds; persist the event and process it in the background.
  • Up to 7 attempts with exponential backoff (10s, 1m40s, 16m40s, 2h46m, then 24h), then a dead-letter queue.
  • Delivery is at-least-once, so deduplicate on payload.id / X-Zernio-Event-Id.
  • payload.id identifies the event only, never a post or account. Route by account.accountId or profiles[].externalId.

Environment Variables

ZERNIO_WEBHOOK_SECRET=your_webhook_secret   # The secret you sent in POST /v1/webhooks/settings

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 2, 2026

View on GitHub →