Logo GenukaGenuka Pay
API Reference

Webhooks

Configure webhook endpoints, verify signatures, and design reliable asynchronous event processing.

Introduction

Webhooks allow Genuka Pay to notify your backend about transaction, payout, KYC, and company events without forcing your system to poll continuously.

Event Delivery

Receive asynchronous updates when transactions, payouts, or compliance states change.

Signature Verification

Validate every incoming delivery before applying state changes in your backend.

Secret Rotation

Regenerate webhook secrets when credentials change or exposure is suspected.

Required Headers

Webhook endpoint management calls should include:

Authenticated Webhook Management Request
X-Public-Key: YOUR_PUBLIC_KEY
X-Timestamp: UNIX_TIMESTAMP
X-Signature: HMAC_SHA256_SIGNATURE

Start with the Webhooks page if you want to manage endpoints from the dashboard.

You can use that page to:

  • create endpoints
  • choose subscribed events
  • enable or disable delivery
  • inspect endpoint metadata
  • rotate the webhook secret

Use the API when you want to automate setup or provision endpoints from your own tooling.

Endpoint Management API

  • GET /api/v1/webhook-endpoints
  • POST /api/v1/webhook-endpoints
  • GET /api/v1/webhook-endpoints/{id}
  • PUT /api/v1/webhook-endpoints/{id}
  • DELETE /api/v1/webhook-endpoints/{id}
  • POST /api/v1/webhook-endpoints/{id}/regenerate-secret

Create a Webhook Endpoint

Create Webhook Endpoint
curl -X POST "{{BASE_URL}}/api/v1/webhook-endpoints" \
  -H "X-Public-Key: YOUR_PUBLIC_KEY" \
  -H "X-Timestamp: UNIX_TIMESTAMP" \
  -H "X-Signature: HMAC_SHA256_SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production backend",
    "target_url": "https://merchant.example.com/webhooks/genuka",
    "events": [
      "transaction.success",
      "transaction.failed",
      "payout.success",
      "kyc.approved"
    ],
    "is_active": true
  }'

Main Request Fields

  • name
  • target_url
  • events
  • is_active
  • metadata

Available Events

  • transaction.success
  • transaction.failed
  • transaction.processing
  • transaction.refunded
  • payout.success
  • payout.failed
  • payout.processing
  • payout.cancelled
  • kyc.submitted
  • kyc.approved
  • kyc.rejected
  • company.created
  • company.suspended
  • company.reactivated

Delivery Payload Shape

Example Webhook Payload
{
  "id": "01HW...",
  "event": "transaction.success",
  "timestamp": "2026-09-19T12:10:00.000000Z",
  "data": {
    "transaction_id": "01HR...",
    "idempotency_key": "...",
    "amount": 10000,
    "net_amount": 9575,
    "provider_fee_amount": 150,
    "genuka_fee_amount": 275,
    "total_fee_amount": 425,
    "currency": "XOF",
    "status": "SUCCESS",
    "completed_at": "2026-09-19T12:10:00.000000Z",
    "metadata": { "external_id": "order_10001" }
  }
}

Your own reference comes back in data.metadata.external_id, the one you sent at initiation.

Delivery headers:

  • Signature — HMAC-SHA256 of the request, see below
  • X-Webhook-Id
  • X-Webhook-Event

Signature Verification

The signature arrives in the Signature header: an HMAC-SHA256 of the raw request body, keyed with the endpoint secret.

verify-webhook.ts
import crypto from "crypto";

// rawBody: the request body exactly as received, before any JSON parsing.
export function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const received = Buffer.from(signature, "utf8");
  const computed = Buffer.from(expected, "utf8");

  return (
    received.length === computed.length &&
    crypto.timingSafeEqual(received, computed)
  );
}

Sign the raw body, not the reparsed object

Re-serialising the JSON after parsing it changes whitespace and key order: the signature will no longer match. Keep the raw body (express.raw(), an unparsed request.body) for verification. Compare in constant time, and reject any unsigned delivery.

Secret Rotation

Rotate Webhook Secret
curl -X POST "{{BASE_URL}}/api/v1/webhook-endpoints/{id}/regenerate-secret" \
  -H "X-Public-Key: YOUR_PUBLIC_KEY" \
  -H "X-Timestamp: UNIX_TIMESTAMP" \
  -H "X-Signature: HMAC_SHA256_SIGNATURE"

Store the regenerated secret immediately. It may not be displayed again later.

Recommendations

  • acknowledge webhook deliveries quickly with a 2xx response
  • push heavy work to a queue or worker
  • verify the signature before mutating business state
  • make your receiver idempotent
  • correlate webhook IDs with internal reconciliation records

How is this guide?

Last updated on