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-Signature header on a KOMOJU webhook?
  • How do I handle payment.captured, payment.refunded or subscription.created events?
  • 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_at" }. 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).

EventMeaning
pingTest event (data is the webhook)
payment.authorizedA payment was authorized
payment.capturedA payment was captured
payment.updatedA payment was changed
payment.expiredA payment was expired
payment.cancelledA payment was cancelled
payment.refund.createdA refund was created (may be partial)
payment.refundedA payment was fully refunded
payment.failedA payment attempt failed (it may be re-attempted)
customer.created / customer.updated / customer.deletedCustomer changes
subscription.created / subscription.captured / subscription.failed / subscription.suspended / subscription.deletedSubscription lifecycle
settlement.createdA 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →