Concepts

Webhooks

Signed, at-least-once, replayable. The backbone of reconciliation.

We deliver events to your HTTPS endpoint as JSON POSTs. Delivery is at-least-once: your receiver must deduplicate byevent.id, which stays stable across retries and manual replays.

Verifying signatures

Each delivery carries a signature header computed with your endpoint’s signing secret. Verify before trusting the payload; reject anything older than 5 minutes to block replays by third parties.

GlobbaPay-Signature header

GlobbaPay-Signature: t=1721825632,v1=5257a869e7…

signed_payload = t + "." + raw_body
expected       = HMAC_SHA256(signing_secret, signed_payload)
valid          = timing_safe_equal(expected, v1) && now - t < 300

Node.js verifier

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(header, rawBody, secret) {
  const { t, v1 } = Object.fromEntries(
    header.split(",").map((p) => p.split("=")),
  );
  const expected = createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  const fresh = Date.now() / 1000 - Number(t) < 300;
  return fresh && timingSafeEqual(
    Buffer.from(expected), Buffer.from(v1),
  );
}

Retries and replay

Non-2xx responses are retried with exponential backoff for 24 hours. Every attempt is visible in the dashboard delivery log with the response code and next retry time, and any event can be replayed manually — same event ID, new delivery.

Event types

FieldTypeDescription
inflow.receivedeventMoney landed in a virtual account. Carries amount, currency, account, payer attributes, ledger_ref.
payout.status.changedeventA payout moved to processing, completed, failed, or cancelled. Failures carry a failure_code.
account.status.changedeventAn account became active, suspended, or closed.
customer.status.changedeventScreening resolved: approved, needs_info, or rejected.