Overview

Webhooks allow your system to establish a communication channel with Nomba, usually via a public URL. When a payment event occurs on your account, Nomba will send a notification via this communication channel to notify you about this event. Nomba will send a POSTrequest to the public webhook URL containing the details of the event and header strictly for verifying that the webhook event originated from the Nomba system.

Established webhook process flow

This image shows an established communication via webhook URL between your system and Nomba.
It is good to note that, you must subscribe for the event type you want to get notified on.

Set up webhook event

To set up your webhooks, navigate to the settings page and select the webhook tab on your dashboard. On this page you can set a live or test webhook URL and signature key. When you add a webhook URL, you can subscribe for the event you want to get notified on.

Set up webhook on your dashboard

Kindly ensure that your webhook URL is publicly available.

Supported Events

  • Payment Success payment_success : Triggered when a payment is successfully credited to your Nomba account, e.g., card transaction, PayByTransfer, or PayByQR.
  • Payout Success payout_success : Triggered when a payment is successfully debited from your account, e.g., funds transfer, bill payment.
  • Payment Failed payment_failed : Triggered when a proposed payment attempt fails.
  • Payment Reversal payment_reversal : Triggered when a payment is reversed from your account back to the customer’s account.
  • Payout Failed payout_failed : Triggered when a payout transaction fails to process successfully and is not completed.
  • Payout Refund payout_refund : Triggered when a payout is refunded back to your Nomba account.

Webhook headers

Every webhook notification from Nomba includes special headers and a payload that matches the content of all supported event types. These headers will help you verify and process the request to ensure that it’s coming from Nomba, as a public URL can be accessed by anyone, so it’s to verify that all webhooks are from Nomba before giving value to your customers. A typical webhook payload will come with the following Nomba-specific headers:
nomba-signature: 0zzATkAuEta5kpKVCExReupW/XglCk/re51P4jiDJ9c=
nomba-sig-value: 0zzATkAuEta5kpKVCExReupW/XglCk/re51P4jiDJ9c=
nomba-signature-algorithm: HmacSHA256
nomba-signature-version: 1.0.0
nomba-timestamp: 2023-03-31T05:56:47Z

HeaderDescription
nomba-signatureA signature created using the signature key configured while creating the webhook on the Nomba dashboard
nomba-signature-algorithmThe algorithm used to generate the signature. Value is always HmacSHA256
nomba-signature-versionThe version of the signature used. Value is 1.0.0 at the moment. It will keep updating as the signing process updates
nomba-timestampAn RFC-3339 timestamp that identifies when the payload was sent.
  • The RFC-3339 format specifies that dates should be represented using the year, month, and day, separated by hyphens, followed by a “T” to separate the date from the time, and then the time represented in hours, minutes, and seconds, separated by colons, with an optional fractional second component. Example; 2022-01-01T15:45:22Z
  • HTTP header names are case insensitive. Your client should convert all header names to a standardized lowercase or uppercase format before trying to determine the value of a header.
Since webhooks are simply HTTP POST requests, there’s a chance that malicious actors could try to send fake webhook events to your server. To protect you from this, Nomba signs each webhook payload using the signature key you set when creating the webhook. The generated signature is included in the request headers, so your server can verify that the request truly came from Nomba and not an attacker.
We recommend configuring the signature key while creating a webhook URL. While this configuration is optional, it is important to configure the keys and verify the signature of the payloads in order to prevent DDoS or Man-in-the-Middle attacks.

Webhook payload

The content of the payload is a JSON object and it gives details about the event that has been triggered.
FieldTypeDescription
event_typeStringThe event type that was triggered
request_idString (UUID)A unique request identifier useful for tracking messages
dataObject (JSON)An object describing the details of the triggered event
{
    "event_type": "payout_success",
    "requestId": "f45a5f24-c8a7-4cce-9ae2-c1**********",
    "data": {
        "merchant": {
            "walletId": "6400f***********",
            "walletBalance": 8***.08,
            "userId": "b9027fce-9860-4b9e-b193-*******"
        },
        "terminal": {},
        "transaction": {
            "fee": 15,
            "type": "transfer",
            "transactionId": "API-TRANSFER-AEAA2-350b8c6b-f31f-4cd0-a802-9************",
            "responseCode": "",
            "originatingFrom": "api",
            "merchantTxRef": "LSQRMHHTLSOQ1757521351751",
            "transactionAmount": 42500,
            "narration": "From Ekuma Fr",
            "time": "2025-09-10T16:22:34Z"
        },
        "customer": {
            "bankCode": "011",
            "senderName": "Ekuma ******",
            "recipientName": "OKORO ******* ******",
            "bankName": "First Bank of Nigeria",
            "accountNumber": "**********"
        }
    }
    "id": ********,
    "eventType": "payout_success",
    "coreUserId": "b9027fce-9860-4b9e-b193-7c**********",
    "businessName": "********** Limited",
    "status": "PUSHED",
    "responseHttpStatus": 200,
    "responsePayload": {
        "status": 200,
        "data": {
            "successful": true
        }
    },
    "email": "co********@gmail.com",
    "eventId": 25,
    "createdOn": "2025-09-10T16:22:34.370304",
    "webhookUrl": "https://api.************",
    "hookRequestId": "f45a5f24-c8a7-4cce-9ae2-************",
    "webhookUrlId": 7
}

Webhook signature verification

To make sure a webhook truly comes from Nomba and hasn’t been altered, each request we send includes a signature in the header. This signature is generated using your webhook payload and the secret key you set on your dashboard. On your end, verification is straightforward:
  1. Re-create the signature : Use the same secret key and payload to generate a hash - HMAC signature.
  2. Compare signatures : Match your generated hash with the nomba-signature header we sent. If they’re the same, you can trust the webhook.
The tab below contains sample code demonstrating how to calculate the HMAC signature and compare it with the signature sent via the webhook.
 package main
 import (
         "crypto/hmac"
         "crypto/sha256"
         "encoding/hex"
         "fmt"
 )

 func main() {
         signatureKey := "your_secret_key" // Replace with your actual signature key
         payload := map[string]interface{}{
                 "event_type": "some_event",
                 "requestId": "12345",
                 // ... other payload fields
         }
         timestamp := "1690646400" // Replace with actual timestamp

         hashingPayload := fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s:%s",
                 payload["event_type"],
                 payload["requestId"],
                 payload["data"]["merchant"]["userId"],
                 payload["data"]["merchant"]["walletId"],
                 payload["data"]["transaction"]["transactionId"],
                 payload["data"]["transaction"]["type"],
                 payload["data"]["transaction"]["time"],
                 payload["data"]["transaction"]["responseCode"])

         message := fmt.Sprintf("%s:%s", hashingPayload, timestamp)

         mac := hmac.New(sha256.New, []byte(signatureKey))
         mac.Write([]byte(message))
         hashed := mac.Sum(nil)
         digest = base64.StdEncoding.EncodeToString(hashed)
}

Idempotency in Nomba

Nomba allows you to pass an idempotency key using the X-Idempotent-key header. This helps prevent duplicate requests when the first request fails due to issues like network interruptions. Although our system already handles idempotency internally, we recommend that you include an idempotency key when calling endpoints such as Bank Transfer. For example, if a bank transfer request succeeds but the confirmation is lost, resending the same request with the same idempotency key ensures that:
  • Only the first request is processed.
  • A duplicate request will either return the original response if identical or throw an error if different.
This keeps your transactions safe and predictable by avoiding accidental duplicate transfers.
Always use a unique idempotency key for each request. This is best practice to ensure consistent behavior.
The following examples show how to generate a unique keys in popular programming languages.
package main

import (
	"fmt"
	"github.com/google/uuid"
)

func main() {
	idempotentKey := uuid.New().String()
	fmt.Println(idempotentKey)
}

Retrying Failed Webhooks

When a webhook fails to deliver because the receiving server does not return a 2XX status code, Nomba automatically retries the request using an exponential backoff policy. This approach spaces out retries with increasing delays, preventing your server from being overwhelmed while still ensuring delivery. Both 4XX client errors and 5XX server errors will trigger this retry flow. After the first failed attempt, Nomba will make up to five additional attempts to re-deliver the webhook. The table below gives proper perspective into how failed webhooks would be retried.
No of RetriesWaitTime (in Seconds)WaitTime (in Mins)
1120 secs2 mins
2280 secs~ 5 mins
3640 secs~ 11 mins
41440 secs24 mins
53200 secs~ 53 mins