Agent skill
Synctera Webhooks Skill
Receive and verify Synctera webhooks. Use when setting up Synctera webhook handlers, debugging Synctera-Signature verification, or handling banking events like ACCOUNT.UPDATED or TRANSACTIONS.POSTED.CREATED.
Install this skill
npx skills add hookdeck/webhook-skills --skill synctera-webhooks
When to Use This Skill
- How do I receive Synctera webhooks?
- How do I verify Synctera webhook signatures (
Synctera-Signature)? - How do I handle
ACCOUNT.UPDATEDorTRANSACTIONS.POSTED.CREATEDevents? - Why is my Synctera webhook signature verification failing?
- How do I generate a Synctera webhook signing secret?
Verification (core)
Synctera uses a custom HMAC scheme (not Standard Webhooks). Each delivery has two headers:
Synctera-Signature— the hex-encoded signature (two.-delimited signatures during secret rotation)Request-Timestamp— POSIX seconds used in the signed string
The signed string is `${Request-Timestamp}.${raw_body}` (the . is a literal separator). Compute HMAC-SHA256(secret, signed_string) and hex-encode. The secret is not your API key — generate it with POST /v0/webhook_secrets (empty body) and store it. Verify against the raw body; don't JSON.parse first.
const crypto = require('crypto');
// secret comes from POST /v0/webhook_secrets (NOT your API key)
function verifySynctera(rawBody, signatureHeader, timestamp, secret, toleranceSec = 300) {
if (!signatureHeader || !/^\d+$/.test(String(timestamp))) return false;
// Replay protection: Request-Timestamp must be within 5 minutes of now
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - Number(timestamp)) > toleranceSec) return false;
// HMAC over `${timestamp}.${rawBody}` — the "." is a literal separator
const expected = crypto.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`).digest('hex');
// During a rolling secret, the header holds two "."-delimited signatures
return signatureHeader.split('.').some((sig) => {
try { return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); }
catch { return false; }
});
}
For complete handlers with route wiring, event dispatch, and tests, see:
Common Event Types
Event names use the format <resource>.[<sub-resource>.]<action>. Wildcards like CUSTOMER.* auto-subscribe to all current and future events under a resource. The three-segment TRANSACTIONS.POSTED.CREATED shows the optional sub-resource case.
Only
ACCOUNT.UPDATEDandTRANSACTIONS.POSTED.CREATEDare verified names. The others below are illustrative of the format only — confirm the exact spelling against Synctera's docs or your own webhook config before subscribing or switching on them. Do not treat this as an authoritative catalog.
| Event | Verified? | Triggered When |
|---|---|---|
ACCOUNT.UPDATED | ✅ verified | An account changed (status, balance limits, etc.) |
TRANSACTIONS.POSTED.CREATED | ✅ verified | A posted transaction was recorded (note plural TRANSACTIONS, three segments) |
CARD.CREATED | illustrative | A card was issued (confirm name) |
CARD.UPDATED | illustrative | A card changed — status, activation (confirm name) |
DISPUTE.CREATED | illustrative | A dispute was opened (confirm name) |
CUSTOMER.* | illustrative | Any customer event (wildcard form) |
For the full event reference, see references/overview.md and Synctera's Webhooks guide.
Environment Variables
# Signing secret from POST /v0/webhook_secrets (NOT your API key)
SYNCTERA_WEBHOOK_SECRET=your_signature_secret_here
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 synctera --path /webhooks/synctera
Reference Materials
- references/overview.md - Synctera webhook concepts and event types
- references/setup.md - Create the secret and register endpoints via the API
- references/verification.md - Signature verification details and gotchas