Agent skill
Unit Webhooks Skill
Receive and verify Unit (unit.co embedded banking) webhooks. Use when setting up Unit webhook handlers, debugging X-Unit-Signature HMAC-SHA1 verification, or handling events like account.created, customer.created, transaction.created, payment.clearing, and application.approved.
Install this skill
npx skills add hookdeck/webhook-skills --skill unit-webhooks
Unit (unit.co) is an embedded banking API. It sends an HTTP POST to your webhook URL whenever an event occurs (application approved, customer created, transaction created, payment clearing, ...). The body is a JSON:API document whose data is an array of events. If the webhook has a token (required when you create one), Unit signs the entire raw body with HMAC-SHA1 and sends the base64 digest in X-Unit-Signature.
When to Use This Skill
- How do I receive Unit webhooks?
- How do I verify the
X-Unit-Signatureheader? - How do I handle
transaction.created,payment.clearingorapplication.approvedevents? - Why is my Unit webhook signature verification failing?
- How do I handle batched (
AtLeastOnce) Unit webhook deliveries?
Signature at a Glance
| Property | Value |
|---|---|
| Header | X-Unit-Signature |
| Algorithm | HMAC-SHA1, base64-encoded (no prefix) |
| Signed content | The entire raw request body (no timestamp) |
| Key | The webhook token, used as a UTF-8 string (not decoded) |
| Timestamp / replay protection | None; dedupe on the event id |
| SDK helper | None; verify manually |
Verification (core)
const crypto = require('crypto');
// rawBody: Buffer from express.raw({ type: '*/*' }) or Buffer.from(await request.arrayBuffer())
function verifyUnitSignature(rawBody, signatureHeader, token) {
if (!signatureHeader || !token) return false;
const expected = crypto.createHmac('sha1', token).update(rawBody).digest('base64');
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
import base64, hashlib, hmac
def verify_unit_signature(raw_body: bytes, signature: str | None, token: str) -> bool:
if not signature or not token:
return False
digest = hmac.new(token.encode("utf-8"), raw_body, hashlib.sha1).digest()
return hmac.compare_digest(base64.b64encode(digest), signature.encode("utf-8"))
Read the raw body with express.raw({ type: '*/*' }) so any content type (application/json or application/vnd.api+json) verifies. Parse JSON only after the signature passes. Return HTTP 200 on success; with AtMostOnce delivery any other status means the event is never retried.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
Each item in data has id, type, attributes and relationships.
| Event | Triggered When |
|---|---|
application.approved | An application is approved and the customer is created |
application.denied | An application is denied |
customer.created | A customer is created |
account.created | A deposit account is created |
transaction.created | A transaction is posted to an account |
payment.clearing | An ACH payment moves to clearing |
payment.returned | A payment is returned |
Unit adds event types over time, so ignore ones you do not handle. See references/overview.md for the full list.
Environment Variables
UNIT_WEBHOOK_TOKEN=your_webhook_token # The `token` you set when creating the webhook
Local Development
npx hookdeck-cli listen 3000 unit --path /webhooks/unit
Register the public URL the CLI prints as the webhook url in the Unit dashboard or POST /webhooks.
Reference Materials
- references/overview.md - Payload shape, delivery modes, event list
- references/setup.md - Create a webhook and token in Unit
- references/verification.md - Signature details and gotchas