Agent skill
Swan Webhooks Skill
Receive and authenticate Swan webhooks. Use when setting up a Swan embedded banking webhook endpoint, checking the x-swan-secret header, debugging 401s, or handling Transaction.Booked, Account.Created, AccountHolder.Updated, Card.Created and Onboarding.Updated events.
Install this skill
npx skills add hookdeck/webhook-skills --skill swan-webhooks
Swan, the embedded banking platform, sends a thin JSON webhook when a resource changes in your project: a transaction is booked, an account or card is created, an onboarding is updated. Webhooks are not signed. If the subscription has a webhook secret, Swan sends it as plain text in the x-swan-secret header of every request, and your endpoint authenticates the request by comparing that header with the secret you stored.
Set a webhook secret on the Swan webhook subscription. Subscriptions without one send nothing to verify.
When to Use This Skill
- How do I receive Swan webhooks?
- How do I verify the
x-swan-secretheader? - Does Swan sign webhook payloads? (No: use the shared secret)
- How do I handle
Transaction.Booked,Account.CreatedorOnboarding.Updatedevents? - Why does my Swan webhook endpoint return 401, or why is there no
x-swan-secretheader?
Verification (core)
const crypto = require('crypto');
// timingSafeEqual throws on different lengths, so check them first.
function safeEqual(a, b) {
const ba = Buffer.from(String(a), 'utf8');
const bb = Buffer.from(String(b), 'utf8');
return ba.length === bb.length && crypto.timingSafeEqual(ba, bb);
}
function verifySwanRequest(headers, secret = process.env.SWAN_WEBHOOK_SECRET) {
if (!secret) return false; // never accept when unconfigured
const received = headers['x-swan-secret']; // the secret, plain text, whole value
return typeof received === 'string' && received.length > 0 && safeEqual(received, secret);
}
- Header:
x-swan-secret: <secret>(case-insensitive; compare the whole value, no prefix). - In Python use
hmac.compare_digeston bytes (.encode("utf-8")both sides;strraises on non-ASCII). - Return
401on a missing or different header,400for a body that isn't a JSON object,200once handled. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it. - A static shared secret authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, deduplicate on
eventId, and rotate the secret if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Notification Payload
Notifications are thin. Query the Swan API with resourceId for the details.
{ "eventType": "Transaction.Booked", "eventId": "...", "eventDate": "2021-08-18T09:35:46.673Z", "projectId": "...", "resourceId": "..." }
- Respond
200 OKwithin 10 seconds; errors are retried every 30 seconds, up to 7 times. - Delivery is at-least-once and unordered: deduplicate on
eventIdand don't assume order.
Event Types
eventType is <Resource>.<Action>.
| Event | Sent When |
|---|---|
Transaction.Booked | A transaction is booked |
Transaction.Pending | A transaction is pending |
Transaction.Rejected | A transaction is rejected |
Account.Created | An account is created |
AccountHolder.Updated | An account holder is updated |
Card.Created | A card is created |
Onboarding.Updated | An onboarding is updated |
Swan documents many more (Consent.Granted, Account.Closing, MerchantPayment.Captured, ...); see references/overview.md.
Environment Variables
SWAN_WEBHOOK_SECRET=your-webhook-secret # the secret set on the Swan webhook subscription
Local Development
Swan can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 swan --path /webhooks/swan
Use the printed URL as the endpoint of the Swan webhook subscription.
Resources
- references/overview.md - What Swan webhooks are, events and payload shape
- references/setup.md - Set the webhook secret and add the subscription
- references/verification.md - Secret check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests