Back to home
INTEGRATION
PAYMENT FEATURES
AFTER PAYMENT
Webhook V2
Webhook Events enable real-time communication between SeerBit and your application. When specific activities occur within the payment ecosystem such as transactions, refunds, wallet movements, disputes, or virtual account events, SeerBit sends a structured notification to your configured
webhook endpoint.
Webhook V2 provides a standardized acknowledgment protocol designed to ensure reliable delivery, traceability, and idempotent handling.
webhook endpoint.
Webhook V2 provides a standardized acknowledgment protocol designed to ensure reliable delivery, traceability, and idempotent handling.
1. Overview
A webhook is an HTTP callback: a POST request sent to your server when an event happens. When your webhook URL is configured in SeerBit,
the platform automatically notifies your system whenever a subscribed event occurs.
When your endpoint receives a webhook request:
the platform automatically notifies your system whenever a subscribed event occurs.
When your endpoint receives a webhook request:
- Your server must respond with a 200 OK
- Your server must return a JSON acknowledgment object
- SeerBit considers the event delivered upon receiving a valid acknowledgment
- Your server processes the event
If the acknowledgment is missing, invalid, or delayed, SeerBit retries delivery.
2. Delivery Architecture
- Event Triggered
A payment, refund, settlement, dispute, or wallet event occurs.
- Webhook Preparaion
SeerBit constructs the event payload and generates an optional acknowledgment reference
- Delivery Attempt
A POST request is sent to your Webhook URL over HTTPS
- Merchant Processing
Your server receives the request and process the event
- Acknowledgment Response
Your server returns:
1{
2 "ackReference": "...",
3 "status": "received"
4}- Confirmation
SeerBit mark the Webhook as delivered
- Retry (if acknowledgment fails)
Multiple retries occur at increasing intervals.
This handshake ensures that events are not lost and can be processed reliably and idempotently.
3. Webhook Request Format
HTTP Method
POST
Content-Type
application/json
Header
Type
Requred
Description
Content-Type
string
Yes
Always application/json
X-Expected-Ack-Reference
string
Optional
A unique reference that must be returned in the acknowledgment response
Body
A JSON object containing event metadata and associated data specific to the event type.
4. Acknowledgment Response (Required)
To confirm receipt of a webhook event, your server must return:
- HTTP 200 OK
- A JSON Object
- Within 5 seconds
1{
2 "ackReference": "<value-or-fallback>",
3 "status": "received"
4}Field Definitions
Field
Type
Description
ackReference
string
Must match the value from the X-Expected-Ack-Reference header if provided. If the header is absent, your system must generate a fallback reference.
status
string
Must always be "received".
SeerBit requires this acknowledgment to validate that your system has successfully accepted the event.
5. Retry Logic
If your server:
- Returns non-200 status
- Responds with an invalid acknowledgment
- Times out (exceeds 5 seconds)
- Is unreachable
…SeerBit will retry delivery.
Retry Intervals
Typical retry sequence:
- 30 seconds
- 2 minutes
- 10 minutes
- 30 minutes
- 1 hour (final retry)
If all attempts fail, the webhook is marked as undelivered.
6. Event Types
Below are the supported webhook events, including the exact sample payloads as used in production today.
6.1 Refund Event
The refund event notification body is structured thus:
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "refund",
6 "eventDate": "2020-05-01 12:55:57",
7 "eventId": "0be677f841254a3eb92fab0d0b6ba232",
8 "data": {
9 "amount": "10",
10 "createdAt": "2019-10-24 07:47:49",
11 "transactionRef": "IHrE1571828556059",
12 "description": "I need my money",
13 "type": "FULL_REFUND",
14 "mode": "TEST",
15 "updatedAt": "2019-10-24 07:47:49"
16 }
17 }
18 }
19 ]
20}6.2 Dispute Event
The dispute event notification body is structured thus:
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "dispute",
6 "eventDate": "2020-05-01 12:56:07",
7 "eventId": "da28df9ea5dd4807b59e5761afd7231b",
8 "data": {
9 "evidence": [
10 {
11 "images": [
12 { "image": "" }
13 ]
14 }
15 ]
16 }
17 }
18 }
19 ]
20}6.3 Transaction Event
The transaction event notification body is structured thus
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "transaction",
6 "eventDate": "2024-07-01 08:56:16",
7 "eventId": "e1c98e0ba9364843b7fa8bd8df0e3bc1",
8 "data": {
9 "amount": 922.63,
10 "mobile": "404",
11 "reference": "SBT-T19824129237",
12 "gatewayMessage": "Successful",
13 "publicKey": "SBTESTPUBK_vtI76q2HWA7QHPbqC1M88HC89gllHKfE",
14 "businessName": "opeyemi test",
15 "fee": "13.63",
16 "productId": "",
17 "channelType": "MASTERCARD",
18 "maskedPan": "5123-45xx-xxxx-0008",
19 "sourceIP": "154.113.161.130",
20 "deviceType": "Desktop",
21 "fullname": "ope seun",
22 "email": "seunopeyemi16@gmail.com",
23 "gatewayReference": "SEERLFN8WWTSILB7ZG7",
24 "country": "NG",
25 "currency": "NGN",
26 "narration": "",
27 "createdAt": "2024-07-01T08:56:07",
28 "updatedAt": "2024-07-01T08:56:13.574",
29 "lastFourDigits": "0008",
30 "cardBin": "512345",
31 "reason": "Successful",
32 "paymentType": "CARD",
33 "gatewayCode": "00",
34 "code": "00"
35 }
36 }
37 }
38 ]
39}6.4 Wallet Transaction Event
The wallet transaction event notification body is structured thus
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "transaction.wallet",
6 "eventDate": "2020-05-01 12:52:28",
7 "eventId": "c472deceabf44924901b104523af14df",
8 "data": {
9 "amount": "100.00",
10 "mobile": "08033456500",
11 "reference": "shh3332hwhwhh22hjjjwj",
12 "gatewayMessage": "APPROVED",
13 "publicKey": "hhw33y2x",
14 "bankCode": "000016",
15 "description": "wallet transaction",
16 "fee": "2.00",
17 "type": "Transfer",
18 "fullname": "John Doe",
19 "email": "peter.diei@centricgateway.com",
20 "country": "NG",
21 "currency": "NGN",
22 "origin": "string",
23 "internalRef": "string",
24 "creditAccountName": "Test Account",
25 "creditAccountNumber": "23221122321",
26 "originatorAccountnumber": "1929383828392",
27 "originatorName": "CGW",
28 "narration": "my narration here",
29 "sessionId": "00002999299388837772828883778",
30 "externalReference": "2203000002992910219",
31 "createdAt": "2019-12-12 16:20:59",
32 "updatedAt": "2019-12-12 16:20:59"
33 }
34 }
35 }
36 ]
37}6.5 Recurrent Transaction Event
The recurrent transaction event notification body is structured thus
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "transaction.recurrent",
6 "eventDate": "2020-05-01 12:50:33",
7 "eventId": "30a33df05b0c465c8c38f4113621685a",
8 "data": {
9 "amount": "150",
10 "mobile": "08033456500",
11 "reference": "TESTPilotR251218123PPOIU149",
12 "publicKey": "SBTESTPUBK_dhrpzbRpR34l6VmqkCFOKA94L5E1jSTu",
13 "description": "Pilot Test Subscription",
14 "productId": "Terrain",
15 "maskedPan": "5123-45xx-xxxx-0008",
16 "email": "akintoyekolawole@gmail.com",
17 "gatewayReference": "F325090871582705056234",
18 "country": "NG",
19 "narration": "Reccurrent",
20 "createdAt": "2020-02-26T09:17:30",
21 "updatedAt": "2020-02-26T09:18:26.496",
22 "lastFourDigits": "0008",
23 "cardBin": "512345"
24 }
25 }
26 }
27 ]
28}6.6 Recurring Debit Transaction Event
The recurrent debit transaction event notification body is structured thus
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "transaction.recurring.debit",
6 "eventDate": "2020-05-01 12:55:32",
7 "eventId": "799f8cad23bc4bc389280f996d81ea55",
8 "data": {
9 "amount": "2000",
10 "reference": "PILOT76558370651618723659",
11 "gatewayMessage": "Successful",
12 "publicKey": "SBTESTPUBK_dhrpzbRpR34l6VmqkCFOKA94L5E1jSTu",
13 "description": "Authorised charge",
14 "channelType": "Recurring Debit",
15 "maskedPan": "5123--4xx-xxxx-xx-0",
16 "type": "TOKEN",
17 "fullname": "Frank Gboyega",
18 "email": "gboyega@fcmb.com",
19 "gatewayReference": "F786046901582644089560",
20 "country": "NG",
21 "currency": "NGN",
22 "narration": "Authorised charge",
23 "createdAt": "2020-02-25T16:21:23",
24 "updatedAt": "2020-02-25T16:21:31.319",
25 "paymentType": "card"
26 }
27 }
28 }
29 ]
30}6.7 Virtual Account Transaction Event
The virtual account transaction event notification body is structured thus
1{
2 "notificationItems": [
3 {
4 "notificationRequestItem": {
5 "eventType": "transaction",
6 "eventDate": "2023-10-06 12:56:47",
7 "eventId": "88bf9852405143bd99502c378b316fdd",
8 "data": {
9 "amount": 100,
10 "country": "NG",
11 "creditAccountName": "BusinessName(Customer Name)",
12 "creditAccountNumber": "4018013418",
13 "currency": "NGN",
14 "email": "xyx@email.com",
15 "externalReference": "GT-012",
16 "fullname": "Adamu Bola Ciroma",
17 "gatewayCode": "00",
18 "code": "00",
19 "internalRef": "",
20 "gatewayMessage": "Successful",
21 "mobile": "404",
22 "narration": "",
23 "origin": "",
24 "originatorAccountnumber": "<Account Number>",
25 "originatorName": "<Account Name>",
26 "publicKey": "<PublicKey>",
27 "reference": "GT-012_SBT_9ADPCIV269",
28 "reason": "Successful"
29 }
30 }
31 }
32 ]
33}7. Sample Implementations
Below are full sample implementations demonstrating how to handle webhook V2 acknowledgments.
1from fastapi import FastAPI, Request, Header
2from fastapi.responses import JSONResponse
3import time
4
5app = FastAPI()
6
7@app.post("/webhook")
8async def receive_webhook(request: Request, x_expected_ack_reference: str = Header(default=None)):
9 body = await request.body()
10 print("Received V2 webhook:", body.decode())
11 print("Received Ack Reference from header:", x_expected_ack_reference)
12
13 if not x_expected_ack_reference:
14 x_expected_ack_reference = f"fallback-ref-{int(time.time() * 1000)}"
15
16 return JSONResponse({
17 "ackReference": x_expected_ack_reference,
18 "status": "received"
19 })1const express = require("express");
2const app = express();
3
4app.use(express.json());
5
6app.post("/webhook", (req, res) => {
7 const body = req.body;
8 let ackRef = req.headers["x-expected-ack-reference"];
9
10 console.log("Received V2 webhook:", JSON.stringify(body));
11 console.log("Received Ack Reference from header:", ackRef);
12
13 if (!ackRef) {
14 ackRef = `fallback-ref-${Date.now()}`;
15 }
16
17 res.json({
18 ackReference: ackRef,
19 status: "received"
20 });
21});1@RestController
2public class WebhookController {
3
4 @PostMapping("/webhook")
5 public ResponseEntity<?> receiveWebhook(
6 @RequestBody String body,
7 @RequestHeader(value = "X-Expected-Ack-Reference", required = false) String ackRef) {
8
9 System.out.println("Received V2 webhook: " + body);
10 System.out.println("Received Ack Reference from header: " + ackRef);
11
12 if (ackRef == null || ackRef.isEmpty()) {
13 ackRef = "fallback-ref-" + System.currentTimeMillis();
14 }
15
16 Map<String, String> response = new HashMap<>();
17 response.put("ackReference", ackRef);
18 response.put("status", "received");
19
20 return ResponseEntity.ok(response);
21 }
22}8. Security Considerations
Although webhook V2 does not require a signature by default, the following best practices are strongly recommended:
Inbound Request Validation
- Enforce HTTPS
- Reject requests not coming from approved IP ranges (if applicable)
- Validate Content-Type = application/json
- Validate the structure of the payload
Idempotency
Your webhook consumer must ensure duplicate events are not processed twice.
Recommended approaches:
Recommended approaches:
- Use eventId field as a unique identifier
- Store processed event IDs in a database
- Check before applying business logic
Logging
log:
- Full payload
- Ack reference
- Processing time
- Errors and decisions
9. Testing Webhooks
To test locally:
Use tools like:
- ngrok
- cloudflared tunnel
- localtunnel
This exposes your local server to receive webhook events.
Replay events
Your internal systems should allow replay during testing, especially for integration and QA teams.
10. Troubleshooting
Webhook not arriving
- Check that your URL is reachable publicly
- Check SSL certificates
- Verify URL configuration in SeerBit dashboard
- Confirm firewall/NAT rules
Webhook arrives but no retries
Your server may be returning 200 OK with incorrect acknowledgment structure.
Event processed twice
Implement idempotency based on the eventId field.
400 or parsing errors
Ensure your endpoint expects JSON and does not require authentication unless pre-agreed.
Need something else?
If you have any questions or need general help, visit our support page
Signup for developer update
You can unsubscribe at any time. Read our privacy policy.