Skip to main content
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

Delivery request

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

Headers

Body envelope

The body is an envelope wrapping the resource that triggered the event:
  • 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.
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.

Source IP

All Kwugwo outbound traffic - both webhook deliveries and any server-to-server API calls we make on your behalf - originates from a single static IP:
If your endpoint sits behind an allowlist, whitelist this address so Kwugwo can reach it. The same IP is used in sandbox and live.

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. 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.