Agent skill

Dwolla Webhooks Skill

Receive and verify Dwolla webhooks. Use when setting up Dwolla webhook handlers, debugging X-Request-Signature-SHA-256 verification, or handling ACH payment events like customer_created, customer_transfer_completed, and customer_funding_source_verified.

Install this skill

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


When to Use This Skill

  • How do I receive Dwolla webhooks?
  • How do I verify the X-Request-Signature-SHA-256 header on a Dwolla webhook?
  • How do I handle customer_created or customer_transfer_completed events?
  • Why is my Dwolla webhook signature verification failing?
  • How do I deduplicate Dwolla webhooks by event ID?

Verification (core)

Dwolla computes an HMAC-SHA256 of the raw request body, keyed with the secret you supplied when creating the webhook subscription (used as a raw string), and sends the lowercase hex digest in X-Request-Signature-SHA-256. There is no timestamp and no prefix. Dwolla has no webhook-verification SDK helper, so verify manually. IP allowlisting is not supported because Dwolla's IPs are dynamic.

Node:

const crypto = require('crypto');

function verify(rawBody, signature, secret) {
  if (!signature) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}

Python:

import hmac, hashlib

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

Never re-encode the JSON before verifying; Dwolla's docs warn that re-encoded bodies produce a different signature.

For complete handlers with route wiring, event dispatch, and tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

The event type is in the payload's topic field (also sent in the X-Dwolla-Topic header).

TopicDescription
customer_createdA Customer was created
customer_verifiedA Customer was verified
customer_funding_source_addedA funding source was added to a Customer
customer_funding_source_verifiedA funding source was verified
customer_transfer_createdA transfer was created for a Customer
customer_transfer_completedA Customer transfer succeeded
customer_transfer_failedA Customer transfer failed
customer_bank_transfer_completedA bank transfer for a Customer succeeded

For the full topic list, see Dwolla Webhook Events.

Payload and Headers

Payload: id (event ID), resourceId, topic, timestamp, and _links (self, account, resource, customer). Fetch the resource through _links.resource.href for current state.

HeaderDescription
X-Request-Signature-SHA-256Hex HMAC-SHA256 of the raw body
X-Dwolla-TopicEvent topic (e.g. customer_created)

Respond with a 2xx within 10 seconds. Dwolla retries up to 8 times over 72 hours and pauses the subscription after 400 consecutive failures (with 24 hours since the last success). Deduplicate on the event id, not resourceId or topic, and return 200 for duplicates.

Environment Variables

DWOLLA_WEBHOOK_SECRET=your_subscription_secret   # The "secret" sent when creating the webhook subscription

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 2, 2026

View on GitHub →