Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kwugwo.africa/llms.txt

Use this file to discover all available pages before exploring further.

You register webhook endpoints in the dashboard. From then on, every qualifying event in your workspace is delivered to that endpoint as an HTTP POST. This page explains the request shape, how to verify it, and how retries work.
Endpoint management — creating, listing, regenerating secrets — is done from the dashboard. The /v1/* merchant API does not expose webhook CRUD.

Event types

EventFires when
nzube.onye.createdA customer record is created.
nzube.onye.updatedA customer record is updated.
nzube.onye.deletedA customer record is deleted.
ugwo.createdA new ugwo is created.
ugwo.updatedAn ugwo changes state (e.g. requires_ugwoprocessingugwo_successful).
ugwo.activity.createdAn activity is created against an ugwo.
ugwo.activity.updatedAn activity changes state (requires_actionsuccessful, etc).

Delivery request

Every webhook is sent as an HTTP POST with a JSON body.

Headers

HeaderPurpose
Content-TypeAlways application/json.
X-Kwugwo-SignatureHMAC-SHA256 of the raw body, hex-encoded, computed with your endpoint’s secret. Empty string if you never set a secret.
X-Kwugwo-Delivery-IdUUID identifying this delivery attempt. Re-delivery on retry uses a new id. Useful for idempotency on your side.

Body envelope

The body is an envelope wrapping the resource that triggered the event:
{
  "uid": "evt.VCvr.7K2qPmRtV9xLnQ8sD1cYwHfE",
  "event": "ugwo.activity.updated",
  "data": {
    /* The resource snapshot. For ugwo.* events this is an ugwo;
       for ugwo.activity.* events this is an activity; for
       nzube.onye.* events this is an onye. */
  }
}
  • uid — the event id. It follows Kwugwo’s standard ID format with the evt prefix and is stable across redelivery attempts — use it as your idempotency key.
  • event — the event type from the table above.
  • data — the resource snapshot, in the same shape the corresponding GET endpoint returns.

Verifying the signature

Compute HMAC-SHA256(secret, raw_body) and compare it constant-time against the X-Kwugwo-Signature header. Verify against the raw body bytes, not a re-encoded JSON string — re-serializing will change whitespace and break the signature.
import crypto from "node:crypto";

export function verifyKwugwoSignature(rawBody, header, secret) {
  if (!secret) return true; // signature is empty when you've not set a secret
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(header, "hex"),
  );
}
If your framework reads the body before you do (e.g. Express’s body-parser), make sure you can still get at the raw bytes. In Express, attach bodyParser.json({ verify: (req, _res, buf) => req.rawBody = buf }) and verify against req.rawBody.

Retries

Kwugwo treats any 2xx response from your endpoint as success. Any other status, a redirect, a network error, or a timeout is a failure.
SettingValue
Max attempts3 (the initial delivery + 2 retries)
Retry interval30 minutes between attempts
BackoffFixed (no exponential growth)
Per-event statuspendingsuccess on a 2xx, or pending again until the cap, then failed
After the third failed attempt the event is marked failed and is not retried again. Re-delivery is not yet exposed in the dashboard; if you need an event replayed, contact support with the event uid.

Best practices

  • Respond quickly. Return 200 as soon as you’ve persisted the event UID. Do the heavy work asynchronously — webhook delivery treats any timeout as a failure and burns one of your three attempts.
  • Be idempotent on event.uid. Two attempts of the same event share the same uid. Two events for the same state change (e.g. ugwo.updated followed by ugwo.activity.updated) do not.
  • Re-fetch on the merchant API for the source of truth. The data snapshot is what the resource looked like at the moment the event fired. If your handler runs minutes later (or after a retry), pull the latest from the merchant API before acting.
  • Don’t enforce a list of allowed event types in code. New events get added over time — just skip the ones you don’t care about.