Agent skill
KOMOJU Webhooks Skill
Receive and verify KOMOJU webhooks. Use when setting up KOMOJU (komoju.com, by Degica) webhook handlers, debugging X-Komoju-Signature verification, or handling payment events like payment.captured, payment.refunded and subscription.created.
Install this skill
npx skills add hookdeck/webhook-skills --skill komoju-webhooks
When to Use This Skill
- How do I receive KOMOJU webhooks?
- How do I verify the
X-Komoju-Signatureheader on a KOMOJU webhook? - How do I handle
payment.captured,payment.refundedorsubscription.createdevents? - Why is my KOMOJU webhook signature verification failing?
- How do I deduplicate KOMOJU webhook deliveries?
Verification (core)
Set a secret token on the KOMOJU webhook. Without one, deliveries are unsigned and fail verification.
With a secret token set, KOMOJU sends X-Komoju-Signature: <hex> with no prefix. The value is the lowercase hex HMAC-SHA256 of the raw request body, keyed with the secret token (its UTF-8 bytes, used as-is). Nothing else is signed: no timestamp, no id. It is not the Standard Webhooks spec. Validate the header's shape (exactly 64 hex characters), then compare in constant time. Verify over the raw bytes before parsing.
Node (compares validated ASCII hex, so Buffer.from(x, 'hex') is never called on untrusted input):
const crypto = require('crypto');
function verifyKomojuSignature(rawBody, signatureHeader, secret) {
if (typeof signatureHeader !== 'string' || !/^[0-9a-f]{64}$/i.test(signatureHeader)) return false;
const expected = Buffer.from(crypto.createHmac('sha256', secret).update(rawBody).digest('hex'));
const received = Buffer.from(signatureHeader.toLowerCase());
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
Python (compare bytes so a non-ASCII header yields 401, not a 500):
import hashlib, hmac, re
def verify_komoju_signature(raw_body: bytes, signature_header: str | None, secret: str) -> bool:
if not signature_header or not re.fullmatch(r"[0-9a-fA-F]{64}", signature_header, re.ASCII):
return False
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest().encode("ascii")
return hmac.compare_digest(signature_header.lower().encode("ascii"), expected)
Missing, empty, malformed or mismatched signature: respond 401. Express handlers must read the body with express.raw({ type: '*/*' }) so any content type verifies the exact bytes.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Events
Every delivery is an HTTPS POST with a JSON body shaped { "id", "type", "resource": "event", "data": { ... }, "created_. Dispatch on the body's type field, which is also sent in the X-Komoju-Event header. data is the resource (its resource key names it, e.g. payment or webhook).
| Event | Meaning |
|---|---|
ping | Test event (data is the webhook) |
payment.authorized | A payment was authorized |
payment.captured | A payment was captured |
payment.updated | A payment was changed |
payment.expired | A payment was expired |
payment.cancelled | A payment was cancelled |
payment.refund.created | A refund was created (may be partial) |
payment.refunded | A payment was fully refunded |
payment.failed | A payment attempt failed (it may be re-attempted) |
customer.created / customer.updated / customer.deleted | Customer changes |
subscription.created / subscription.captured / subscription.failed / subscription.suspended / subscription.deleted | Subscription lifecycle |
settlement.created | A settlement was created |
Delivery and Idempotency
A non-success status or a connection failure is retried 25 times with growing intervals, the final attempt about 25 days later. You can resend a delivery from the dashboard (from a payment's page). Dedupe on the event id, and acknowledge quickly and process asynchronously.
Environment Variables
KOMOJU_WEBHOOK_SECRET=your_secret_token # the secret token set on the KOMOJU webhook
Setup with Hookdeck
Create the Hookdeck source with the KOMOJU type and paste the webhook's secret token into the source's webhook verification settings. Set a secret token on the KOMOJU webhook. Without one, deliveries are unsigned and fail verification. Hookdeck verifies X-Komoju-Signature (HMAC-SHA256, hex) on KOMOJU's POST delivery.
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 komoju --path /webhooks/komoju
Reference Materials
- references/overview.md - KOMOJU webhook concepts, events and payloads
- references/setup.md - Create a webhook in the dashboard and set its secret token
- references/verification.md - Signature verification details and gotchas