Skip to main content

RecovaPro Webhooks

E-mandate operations on RecovaPRO are asynchronous. A mandate is created instantly, but the payer's bank decides whether to honour it (up to 24 hours for micro-deposit authorization, and up to 48 hours for e-signature), and its status keeps changing over the lifetime of the direct debit arrangement. Rather than polling Verify mandate, CreditChek submits a POST request to the webhook URL configured for your RecovaPRO profile each time the state of a mandate changes, so your platform can trigger the matching workflow — activate a loan, notify a payer, close out a repayment schedule.

Your endpoint should respond to webhooks as quickly as possible. To acknowledge receipt of a webhook, your endpoint must return a 2xx HTTP status code. This status code should only indicate receipt of the message, not an acknowledgement that it was successfully processed by your system. Any other information returned in the response headers or response body is ignored.

Configure your webhook URL

RecovaPRO events are only dispatched once Webhook settings for RecovaPRO events have been configured on your merchant profile in the CreditChek SaaS Portal. See Integration options.

Security​

All webhook requests are sent with an x-auth-signature header for verification. It should match the secret key pair of the app secret key you used when creating the mandate. See Webhooks for the shared verification and retry behaviour that applies across all CreditChek services.

Webhook structure​

Every RecovaPRO webhook follows the same envelope as the rest of the CreditChek platform:

FieldTypeDescription
eventstringThe event that determines the structure of data.
dataobjectThe mandate payload for the event.

Mandate lifecycle​

The events below map onto the lifecycle of a single e-mandate. Use mandateId (returned as _id on mandate_created_event) or your own reference to correlate them with the mandate on your side:

mandate_created_event      → status: pending      (awaiting the payer's bank)
↓
mandate_approved_event → active: true (mandate placed on the payer's bank record; collection can begin)
↓
mandate_paused_event → status: paused (auto-collection suspended, mandate still valid)
↓
mandate_canceled_event → status: deactivated (terminated by the merchant, final)
mandate_expire_event → status: closed (endDate reached, final)
Ordering

Webhooks are not guaranteed to arrive in order, and a mandate can move to paused and back any number of times before it is closed or deactivated. Treat each event as a state update keyed on mandateId, and ignore an event that describes a state you have already moved past.

Supported Webhook Events​

RecovaPRO dispatches three tiers of real-time webhooks via our System: Mandate-Level Events (tracking the overarching payment contract), Consent-Level Events (tracking each individual enrolled bank account), and Collection-Level Events (tracking each repayment attempt, whether scheduled by the platform or triggered manually).

Mandate-Level Events​

Event NameTrigger StageDescription
recova.mandate.initializeMandate CreationFires immediately when a mandate contract is initiated via POST /consent/create.
recova.mandate.approvedMandate ActivatedFires when the primary account authorization is confirmed by the bank, putting the mandate into active status.
recova.mandate.pausedCollection PausedFires when scheduled collections are suspended via /pause-mandate.
recova.mandate.resumedCollection ResumedFires when collections are reinstated via /resume-mandate.
recova.mandate.cancelledMandate CancelledFires when the mandate contract is permanently deactivated via /cancel-mandate.
recova.mandate.expireMandate ExpiredFires when the mandate reaches its endDate and transitions to closed.
Event NameTrigger StageDescription
recova.consent.createdAccount EnrolledFires each time a bank account is attached via /micro-deposit/create/mandate or /gsm.
recova.consent.approvedAccount VerifiedFires when the payer completes the ₦50 transfer from that specific account and the bank verifies authorization.
recova.consent.rejectedAccount DeclinedFires if the payer's bank rejects authorization for that specific bank account (e.g. invalid account or bank decline).
recova.consent.expiredAuthorization ExpiredFires if the ₦50 micro-deposit authorization window elapses without completion.
recova.consent.cancelledAccount RevokedFires if authorization for that specific bank account is cancelled or revoked.

Collection-Level Events (Per Repayment — Scheduled or Auto)​

Event NameTrigger StageDescription
recova.collection.notificationAdvance NoticeFires ahead of a scheduled repayment to notify the payer of the upcoming debit, normally 24 hours before it is due.
recova.collection.successCollection SuccessfulFires when a scheduled or manual repayment is debited successfully. More than one event can be sent for a single repayment as partial collections complete.
recova.collection.failedCollection FailedFires when a scheduled or manual repayment attempt is declined by the bank, e.g. Insufficient funds.

Mandate-Level Webhook Events​

Dispatched immediately upon mandate initialization. Sent as soon as the e-mandate request is created and submitted for authorization. The mandate arrives with status: "pending" — no debit can be attempted against it yet. The _id in this payload is the mandateId referenced by every subsequent event and by the mandate API endpoints.

Sample webhook for a initializing a mandate
{
"event": "recova.mandate.initialize",
"data": {
"collectedAmount": 0,
"collectionMode": "auto",
"debitType": "recurring",
"mandateType": "variable",
"authorizationType": "micro-deposit",
"status": "pending",
"corporateAccount": false,
"rcNumber": null,
"businessName": null,
"businessEmail": null,
"businessPhone": null,
"balanceEnquiry": false,
"creditInsurance": false,
"consents": [],
"acceptedTerms": true,
"isDemo": false,
"_id": "77192739b3cf770000000000",
"createdAt": "2026-09-22T12:00:00.000Z",
"updatedAt": "2026-09-22T12:00:00.000Z",
"__v": 0
}
}

Payload fields

FieldTypeDescription
_idstringThe mandate ID. This is the mandateId referenced in all other events and API calls.
referencestringUnique merchant transaction reference for the mandate arrangement.
businessIdstringYour CreditChek business ID.
appIdstringThe app identifier the mandate was created under.
borrowerIdstring | nullThe borrower identifier where the payer exists on your CreditChek customer records.
productIdstringThe product or loan package ID associated with the mandate.
bvnstring11-digit Bank Verification Number of the payer.
startDatestringStart date (YYYY-MM-DD) when collections can begin.
endDatestringExpiry date (YYYY-MM-DD) of the direct debit arrangement.
frequencystringCollection frequency for recurring debits (daily, weekly, or monthly).
totalAmountDuenumberTotal amount collectable over the lifetime of the mandate (in Naira).
outstandingBalancenumberRemaining balance to be collected (in Naira).
collectedAmountnumberTotal amount successfully debited to date (in Naira).
collectionModestringCollection mode (auto for automated scheduler, manual for merchant-triggered debits).
debitTypestringDebit cadence (one-time or recurring).
mandateTypestringMandate installment structure (fixed or variable).
authorizationTypestringPayer authorization channel (micro-deposit or e-signature).
statusstringInitial mandate status (pending awaiting account enrollment and bank authorization).
corporateAccountbooleantrue if the mandate is placed against a corporate bank account.
rcNumberstring | nullCorporate CAC registration number (when corporateAccount is true).
businessNamestring | nullRegistered corporate entity name (when corporateAccount is true).
businessEmailstring | nullCorporate billing email (when corporateAccount is true).
businessPhonestring | nullCorporate contact telephone number (when corporateAccount is true).
balanceEnquirybooleanIndicates whether automated balance checks before debits are enabled.
creditInsurancebooleanIndicates whether the ERM credit-risk protection add-on is attached.
acceptedTermsbooleanConfirmation that the payer accepted terms and conditions.
isDemobooleantrue if initiated using sandbox test credentials. Always verify false in production.
createdAtstringISO 8601 timestamp of mandate creation.
updatedAtstringISO 8601 timestamp of last record update.


Dispatched each time a bank account is attached to a mandate (single account or each individual account within a Multi-Bank GSM enrollment).

{
"event": "recova.consent.created",
"data": {
"_id": "77192739b3cf770000000001",
"businessId": "730c8be89121212121212121",
"appId": "730c8be89131cd1111111111",
"mandateId": "77192739b3cf770000000000",
"bankCode": "044",
"bankName": "ACCESS BANK PLC",
"accountNumber": "1234567890",
"accountName": "John Smith",
"reference": "563315af-f0a4-420f-8e5e-df9b1c28973b",
"email": "[email protected]",
"primary": true,
"createdAt": "2026-09-22T12:05:00.000Z"
}
}

Payload fields

FieldTypeDescription
_idstringUnique identifier for this consent record (consentId).
businessIdstringYour CreditChek business ID.
appIdstringThe app identifier the mandate was created under.
mandateIdstringThe overarching mandate ID to which this bank account is attached.
bankCodestringCBN 3-digit code of the bank.
bankNamestringName of the bank where this account resides.
accountNumberstringThe enrolled 10-digit NUBAN bank account number.
accountNamestringAccount holder name verified by the bank rail.
referencestringThe merchant reference associated with the mandate.
emailstringPayer contact email for notifications.
primarybooleantrue if designated as primary collection account; false for secondary GSM backup accounts.
createdAtstringISO 8601 timestamp of consent enrollment.


Collection-Level Webhook Events​

Sent to the payer ahead of a scheduled collection to notify them of an upcoming debit. Typically dispatched 24 hours before the collection is due.

Sample webhook for an advanced notification
{
"event": "recova.collection.notification",
"data": {
"name": "John Musa",
"email": "[email protected]",
"mandateId": "6287757348cc6c0013f4e0c2",
"totalAmount": 25000,
"dueDate": "2026-09-22",
"reference": "er090405"
}
}

Payload fields

FieldTypeDescription
mandateIdstringThe mandate the upcoming collection will be attempted against.
referencestringThe mandate reference sent at creation.
namestringName of the payer the notification is addressed to.
emailstringEmail address of the payer the notification is addressed to.
totalAmountnumberAmount that will be debited when the collection runs.
dueDatestringDate (YYYY-MM-DD) the collection is scheduled for.

Handling Webhook Events​

Below is a complete Node.js Express implementation pattern showing how to securely verify signatures, immediately acknowledge receipt with a 200 status, and process lifecycle events idempotently.

const express = require("express");
const router = express.Router();

const secret = process.env.CREDITCHEK_SECRET_KEY;

function verifyWebhook(req, res, next) {
// Verify HMAC signature
if (req.headers["x-auth-signature"] !== secret) {
return res.status(401).json({
message: "Unauthorized request.",
});
}
next();
}

router.post("/recova/webhook", verifyWebhook, (req, res) => {
// 1. Acknowledge receipt immediately before downstream processing
res.sendStatus(200);

const { event, data } = req.body;
const mandateId = data.mandateId || data._id;

switch (event) {
case "recova.mandate.initialize":
// Mandate submitted for authorization — keep payer in pending state
break;

case "recova.mandate.approved":
// Mandate is active on data.accountNumber — activate loan or subscription
break;

case "recova.mandate.paused":
// Collection suspended — pause automated repayment schedule
break;

case "recova.mandate.resumed":
// Collection reinstated — resume active repayment schedule
break;

case "recova.mandate.cancelled":
case "recova.mandate.expire":
// Final states — close out schedule and cease collections
break;

case "recova.consent.approved":
// Secondary bank account authorized successfully
break;

case "recova.consent.rejected":
case "recova.consent.expired":
case "recova.consent.cancelled":
// Account-level authorization declined or revoked
break;

default:
console.log(`Unhandled webhook event: ${event}`);
}
});

module.exports = router;

[!TIP] Idempotency Webhook events may occasionally be redelivered due to network retries or manual resends from your merchant dashboard. Always key your event processor on mandateId / consentId and the target state so that reprocessing duplicate deliveries is safe and idempotent.


Simulating Mandate Approval in Test Mode​

To simulate bank approval for your sandbox mandate and test your webhook receiver:

POST /v1/recova/consent/business/test/approve-mandate
Content-Type: application/json
token: <YOUR_API_KEY>
{
"mandateId": "77192739b3cf770000000000"
}

This immediately triggers a mock recova.mandate.approved webhook to your configured webhook URL, allowing you to test your downstream loan disbursement workflows safely.

Idempotency

The same event may reach you more than once, for example when a failed delivery is re-sent from your dashboard audit logs. Key your handler on mandateId and the resulting state so that reprocessing an event is harmless.

Sample data

The values in these payloads are illustrative. Use the field names and nesting as the integration contract, and treat the values as mandate-specific.

Contact [email protected] if an expected event does not reach your webhook URL.