Agent skill

Nango Webhooks Skill

Receive and verify Nango webhooks. Use when setting up Nango webhook handlers, debugging X-Nango-Hmac-Sha256 signature verification (HMAC-SHA256 hex of the raw body, keyed with the environment's webhook signing key, not the API key), or handling Nango webhook types: auth (connection creation, override, refresh, deletion), sync (sync run succeeded or failed), forward (external API webhooks forwarded through Nango), and async_action.

Install this skill

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


When to Use This Skill

  • How do I receive Nango webhooks in my app?
  • How do I verify the X-Nango-Hmac-Sha256 webhook signature?
  • Why does Nango webhook signature verification fail? (wrong key, parsed body, legacy header)
  • How do I save the connection ID from an auth / creation webhook?
  • How do I react to a sync webhook and fetch the changed records?
  • How do I handle forward webhooks (HubSpot, Slack, GitHub, etc. events forwarded by Nango)?

Verification (core)

Nango signs the raw request body with HMAC-SHA256, keyed with the environment's webhook signing key (Environment Settings > Webhooks > Signing key) used as a plain UTF-8 string, and sends the lowercase hex digest in X-Nango-Hmac-Sha256. There is no timestamp or prefix. Ignore the legacy X-Nango-Signature header (plain SHA-256, deprecated).

Node (the @nangohq/node SDK does the same thing):

const { Nango } = require('@nangohq/node');

const nango = new Nango({
  apiKey: process.env.NANGO_API_KEY,                        // API calls
  webhookSigningKey: process.env.NANGO_WEBHOOK_SIGNING_KEY, // webhook verification
});
// rawBody must be the unparsed body as a string
const isValid = nango.verifyIncomingWebhookRequest(rawBody, req.headers);

Python (no SDK, manual):

import hmac, hashlib

def verify(raw_body: bytes, signature: str, signing_key: str) -> bool:
    expected = hmac.new(signing_key.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, (signature or "").lower())

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

Webhook Types

Nango has no per-event names; route on the body's type (and operation / success for auth, success for sync). Ignore unknown types: Nango adds new ones without treating it as a breaking change.

typeKey fieldsWhen it fires
authoperation: creation | override | refresh | deletion; success; connectionId; providerConfigKey; tagsConnection created, re-authorized, token refresh failed or recovered, connection deleted
syncsuccess; syncName; model; modifiedAfter; responseResults; checkpoints; errorA sync function run finished (succeeded or failed)
forwardfrom; providerConfigKey; connectionId (optional); payload; unverified (optional)An external API webhook was forwarded through Nango
async_actionconnectionId; providerConfigKey; payloadAn async action finished

auth + creation with success: true is the one you must handle: it is how you learn the new connectionId (match it to your user via tags).

For payload details, see references/overview.md and Nango's webhook docs.

Important Headers

HeaderDescription
X-Nango-Hmac-Sha256Hex HMAC-SHA256 of the raw body (use this)
X-Nango-SignatureLegacy plain SHA-256 signature (do not use)
X-Nango-Webhook-Unverifiedtrue on forwards where Nango could not check the external provider's signature

Environment Variables

NANGO_WEBHOOK_SIGNING_KEY=your_webhook_signing_key   # Environment Settings > Webhooks > Signing key
NANGO_API_KEY=your_nango_api_key                     # Environment Settings > API key (SDK constructor + API calls)

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →