Tracks each SDK method against the Monnify endpoint it calls, for maintainers adding or changing a client (see CONTRIBUTING.md).
Status legend:
- Implemented — shipped, with the path/payload sourced from a live sandbox call or our official API reference.
- Planned — designed in the implementation plan, not yet built.
| SDK Method | Monnify Endpoint | Status | Notes |
|---|---|---|---|
MonnifyTokenProvider.LoginAsync |
POST /api/v1/auth/login |
Implemented | Basic auth (base64(apiKey:secretKey)) |
IMonnifyCollectionsClient.InitializeTransactionAsync |
POST /api/v1/merchant/transactions/init-transaction |
Implemented | Returns a hosted checkoutUrl; contractCode is required |
IMonnifyCollectionsClient.CreateReservedAccountAsync |
POST /api/v2/bank-transfer/reserved-accounts |
Implemented | getAllAvailableBanks is required (bool); if false, preferredBanks must be supplied |
IMonnifyCollectionsClient.GetReservedAccountAsync |
GET /api/v2/bank-transfer/reserved-accounts/{accountReference} |
Implemented | |
IMonnifyCollectionsClient.GetReservedAccountTransactionsAsync |
GET /api/v1/bank-transfer/reserved-accounts/transactions?accountReference=&page=&size= |
Implemented | Item fields modeled from general conventions, not a populated real example |
IMonnifyCollectionsClient.DeleteReservedAccountAsync |
DELETE /api/v1/bank-transfer/reserved-accounts/reference/{accountReference} |
Implemented | Note the /reference/ path segment — easy to miss |
IMonnifyCollectionsClient.CreateInvoiceAsync |
POST /api/v1/invoice/create |
Implemented | Sandbox rejects expiryDate too far in the future |
IMonnifyCollectionsClient.GetInvoiceAsync |
GET /api/v1/invoice/{invoiceReference}/details |
Implemented | |
IMonnifyCollectionsClient.GetInvoicesAsync |
GET /api/v1/invoice/all?page=&size= |
Implemented | |
IMonnifyCollectionsClient.CancelInvoiceAsync |
DELETE /api/v1/invoice/{invoiceReference}/cancel |
Implemented | |
IMonnifyCollectionsClient.InitiateBankTransferAsync |
POST /api/v1/merchant/bank-transfer/init-payment |
Implemented | Real response field is ussdPayment, not ussdCode as our simplified doc sample shows |
IMonnifyCollectionsClient.ChargeAsync |
POST /api/v1/merchant/cards/charge |
Implemented | Requires a transactionReference from InitializeTransactionAsync first. Automatic retry disabled - this directly debits a card |
IMonnifyCollectionsClient.AuthorizeOtpAsync |
POST /api/v1/merchant/cards/otp/authorize |
Implemented | Not sandbox-verified - blocked on the ChargeAsync bin issue above |
IMonnifyCollectionsClient.Authorize3dsAsync |
POST /api/v1/sdk/cards/secure-3d/authorize |
Implemented | Restricted to PCI DSS-certified merchants, requires separate activation |
IMonnifyCollectionsClient.SearchTransactionsAsync |
GET /api/v1/transactions/search?page=&size=&... |
Implemented | Many optional filters (reference, amount range, customer, status, date range) |
IMonnifyCollectionsClient.GetTransactionAsync |
GET /api/v2/transactions/{transactionReference} |
Implemented | Amount fields are quoted strings; decimal via JsonNumberHandling.AllowReadingFromString |
IMonnifyCollectionsClient.QueryTransactionAsync |
GET /api/v2/merchant/transactions/query?transactionReference=&paymentReference= |
Implemented | Same response shape as GetTransactionAsync; requires at least one query parameter |
IMonnifyCollectionsClient.InitiateRefundAsync |
POST /api/v1/refunds/initiate-refund |
Implemented | |
IMonnifyCollectionsClient.GetRefundAsync |
GET /api/v1/refunds/{refundReference} |
Implemented | |
IMonnifyCollectionsClient.GetRefundsAsync |
GET /api/v1/refunds?page=&size= |
Implemented | |
IMonnifyCollectionsClient.CreateLimitProfileAsync |
POST /api/v1/limit-profile/ |
Implemented | Sandbox-verified |
IMonnifyCollectionsClient.GetLimitProfilesAsync |
GET /api/v1/limit-profile/ |
Implemented | pageable/sort are null in the real response — not modelled |
IMonnifyCollectionsClient.UpdateLimitProfileAsync |
PUT /api/v1/limit-profile/{limitProfileCode} |
Implemented | Profile code goes in the path; body has only the four editable fields |
IMonnifyCollectionsClient.CreateReservedAccountWithLimitAsync |
POST /api/v1/bank-transfer/reserved-accounts/limit |
Implemented | Separate from CreateReservedAccountAsync (v2 path); requires limitProfileCode |
IMonnifyCollectionsClient.UpdateReservedAccountLimitAsync |
PUT /api/v1/bank-transfer/reserved-accounts/limit |
Implemented | Sandbox-verified |
IMonnifyCollectionsClient.CreateSubAccountsAsync |
POST /api/v1/sub-accounts |
Implemented | Body and response are both arrays; requires relationship-manager approval for live. Sandbox-confirmed via raw curl (identical payload, same failure) that a fintech/mobile-money bank code as the destination (e.g. 305 PAYCOM/OPay) gets rejected with a generic HTTP 500 / responseCode: "99" rather than a clean validation error - a traditional bank code (e.g. 044 Access Bank) succeeds with the exact same request shape. This is Monnify's own sandbox behavior, not an SDK bug |
IMonnifyCollectionsClient.GetSubAccountsAsync |
GET /api/v1/sub-accounts |
Implemented | Returns flat array (not paged) |
IMonnifyCollectionsClient.UpdateSubAccountAsync |
PUT /api/v1/sub-accounts |
Implemented | subAccountCode included in the PUT body, not the path |
IMonnifyCollectionsClient.DeleteSubAccountAsync |
DELETE /api/v1/sub-accounts/{subAccountCode} |
Implemented | Returns no responseBody — handled via SendVoidAsync |
IMonnifyCollectionsClient.CreateMandateAsync |
POST /api/v1/direct-debit/mandate/create |
Implemented | Sandbox returned mandateStatus: "PENDING" and no redirectUrl, not the "INITIATED" + echoed redirectUrl our docs sample shows |
IMonnifyCollectionsClient.GetMandatesAsync |
GET /api/v1/direct-debit/mandate/?mandateReferences= |
Implemented | The reference field in the response is mandateReference, not externalMandateReference as our docs sample shows. Two undocumented fields found: responseMessage and schemeCode. mandateStatus can be PENDING_AUTHORIZATION, not in our docs' status list |
IMonnifyCollectionsClient.DebitMandateAsync |
POST /api/v1/direct-debit/mandate/debit |
Implemented | Not sandbox-verified end-to-end - needs an ACTIVE mandate. Automatic retry disabled - this directly debits a mandate |
IMonnifyCollectionsClient.GetMandateDebitStatusAsync |
GET /api/v1/direct-debit/mandate/debit-status?paymentReference= |
Implemented | responseMessage can be {} instead of a string - handled via LenientStringJsonConverter |
IMonnifyCollectionsClient.CancelMandateAsync |
PATCH /api/v1/direct-debit/mandate/cancel-mandate/{mandateCode} |
Implemented | Sandbox-verified against a real mandate created in this session |
IMonnifyCollectionsClient.ListMandatesAsync |
GET /api/v1/direct-debit/mandates?startDate=&endDate=&... |
Implemented | Own paging shape; first/numberOfElements/empty are nullable since the sandbox sometimes omits them |
IMonnifyCollectionsClient.CreatePaycodeAsync |
POST /api/v1/paycode |
Implemented | Requires paycode feature activation; clientId in the body is the merchant's API key. Sandbox returns "Unable to process request" if not activated |
IMonnifyCollectionsClient.GetPaycodesAsync |
GET /api/v1/paycode?transactionReference=&beneficiaryName=&transactionStatus=&from=&to= |
Implemented | from/to are unix timestamps; paycode field is masked (e.g. ******46) in list and single-get responses |
IMonnifyCollectionsClient.GetPaycodeAsync |
GET /api/v1/paycode/{paycodeReference} |
Implemented | Returns masked paycode; response omits requestSuccessful field (handled via nullable envelope) |
IMonnifyCollectionsClient.CancelPaycodeAsync |
DELETE /api/v1/paycode/{paycodeReference} |
Implemented | Returns the cancelled paycode object; sandbox discovered undocumented cancelDate field |
IMonnifyCollectionsClient.GetUnmaskedPaycodeAsync |
GET /api/v1/paycode/{paycodeReference}/authorize |
Implemented | Returns the unmasked paycode value (e.g. 04797046 instead of ******46) |
IMonnifyCollectionsClient.ChargeCardTokenAsync |
POST /api/v1/merchant/cards/charge-card-token |
Implemented | Charges a previously tokenized, reusable card in one call - no OTP/3DS follow-up. Response shape matches Transaction exactly (cardDetails.cardToken/.reusable already modeled there). Automatic retry disabled, same reasoning as ChargeAsync - directly debits a card. Not yet sandbox-verified end-to-end (needs a reusable: true card token from a prior successful charge, which the sandbox BIN issue on ChargeAsync currently blocks) |
| (Collections — Payment Links) | N/A | Out of scope | Dashboard-only feature, no API |
IMonnifyDisbursementsClient.InitiateSingleTransferAsync |
POST /api/v2/disbursements/single |
Implemented | Requires Transfer feature activation (sales@monnify.com); automatic retry disabled |
IMonnifyDisbursementsClient.AuthorizeSingleTransferAsync |
POST /api/v2/disbursements/single/validate-otp |
Implemented | For a transfer with status PENDING_AUTHORIZATION |
IMonnifyDisbursementsClient.ResendSingleTransferOtpAsync |
POST /api/v2/disbursements/single/resend-otp |
Implemented | |
IMonnifyDisbursementsClient.GetSingleTransferAsync |
GET /api/v2/disbursements/single/summary?reference= |
Implemented | |
IMonnifyDisbursementsClient.GetSingleTransfersAsync |
GET /api/v2/disbursements/single/transactions?pageNo=&pageSize= |
Implemented | |
IMonnifyDisbursementsClient.InitiateBulkTransferAsync |
POST /api/v2/disbursements/batch |
Implemented | Response includes an undocumented transactionBatchReference field; automatic retry disabled |
IMonnifyDisbursementsClient.AuthorizeBulkTransferAsync |
POST /api/v2/disbursements/batch/validate-otp |
Implemented | Body field is reference, not batchReference, despite this being the batch endpoint |
IMonnifyDisbursementsClient.ResendBulkTransferOtpAsync |
POST /api/v2/disbursements/batch/resend-otp |
Implemented | Body field is batchReference here (unlike validate-otp above) |
IMonnifyDisbursementsClient.GetBulkTransferSummaryAsync |
GET /api/v2/disbursements/batch/summary?reference= |
Implemented | |
IMonnifyDisbursementsClient.GetBulkTransferTransactionsAsync |
GET /api/v2/disbursements/bulk/{batchReference}/transactions?pageSize=&pageNo= |
Implemented | |
IMonnifyDisbursementsClient.GetBulkTransfersAsync |
GET /api/v2/disbursements/bulk?sourceAccountNumber=&pageNo=&pageSize= |
Implemented | Docs show a /transactions suffix on this path; that 404s — omit it |
IMonnifyDisbursementsClient.SearchTransactionsAsync |
GET /api/v2/disbursements/search-transactions?sourceAccountNumber=&... |
Implemented | |
IMonnifyDisbursementsClient.GetWalletBalanceAsync |
GET /api/v2/disbursements/wallet-balance?accountNumber= |
Implemented | Accepts both numeric and quoted-string balances via AllowReadingFromString |
IMonnifyDisbursementsClient.CreateWalletAsync |
POST /api/v1/disbursements/wallet |
Implemented | Requires wallet feature activation; bvnDetails is a nested object |
IMonnifyDisbursementsClient.GetWalletsAsync |
GET /api/v1/disbursements/wallet?pageNo=&pageSize=&walletReference= |
Implemented | 21 wallets in sandbox; createdOn is absent in list items but present on create response |
IMonnifyDisbursementsClient.GetCustomerWalletBalanceAsync |
GET /api/v1/disbursements/wallet/balance?accountNumber= |
Implemented | |
IMonnifyDisbursementsClient.GetWalletTransactionsAsync |
GET /api/v1/disbursements/wallet/transactions?accountNumber=&pageNo=&pageSize= |
Implemented | Balance amounts sent as large integers (e.g. 5000000000); AllowReadingFromString applied defensively |
IMonnifyVerificationClient.ValidateAccountNumberAsync |
GET /api/v2/disbursements/account/validate?accountNumber=&bankCode= |
Implemented | Free on both sandbox and live. Sandbox-confirmed the v1 path also still responds identically, but v2 is the current documented path |
IMonnifyVerificationClient.MatchBvnDetailsAsync |
POST /api/v1/vas/bvn-details-match |
Implemented | Bills the merchant wallet per request |
IMonnifyVerificationClient.MatchBvnToAccountAsync |
POST /api/v1/vas/bvn-account-match |
Implemented | Bills the merchant wallet per request |
IMonnifyVerificationClient.VerifyNinAsync |
POST /api/v1/vas/nin-details |
Implemented | Live environment only; bills the merchant wallet per request |
IMonnifyBanksClient.GetBanksAsync |
GET /api/v1/banks |
Implemented | Most entries have null USSD fields |
IMonnifyBanksClient.GetUssdEnabledBanksAsync |
GET /api/v1/sdk/transactions/banks |
Implemented | Not every entry has a populated ussdTemplate (e.g. Suntrust Bank) |
MonnifyWebhookValidator.IsValid / ComputeSignature |
monnify-signature header |
Implemented | HMAC-SHA512(key: secretKey, message: rawRequestBody), lowercase hex, over the compact (not pretty-printed) JSON body. Our docs prose describes plain SHA-512 — HMAC is correct. Sandbox sends no signature header at all, so IsValid is always false there by design |
MonnifyWebhookParser.Parse / ParseEventData / ParseMetaData |
n/a (parses the request body) | Implemented | 11 classes cover 12 event types (disbursement and refund status events share one class each, differentiated by status). Handles real shape inconsistencies: paymentSourceInformation as array/empty-object/single-object (SingleOrArrayJsonConverter); amount fields as string or number; rejected-payment's created_on (snake_case); wallet-activity's metaData as an envelope sibling, not nested |
HttpRequest.ValidateMonnifyWebhookAsync |
n/a (ASP.NET Core convenience) | Implemented | Reads the body once and doesn't reset its position — for a dedicated webhook endpoint only |
IMonnifyBillsClient.GetBillerCategoriesAsync |
GET /api/v1/vas/bills-payment/biller-categories?page=&size= |
Implemented | Paging is 0-based; our docs incorrectly show a 1-based default of page=1. Real default size is 20, not 10 |
IMonnifyBillsClient.GetBillersAsync |
GET /api/v1/vas/bills-payment/billers?page=&size=&category_code= |
Implemented | Same 0-based paging note as above |
IMonnifyBillsClient.GetBillerProductsAsync |
GET /api/v1/vas/bills-payment/biller-products?biller_code=&page=&size= |
Implemented | category/biller are singular objects, not the arrays our docs show. biller_code accepts either a plain code (AIRTEL) or a biller--prefixed slug (biller-dstv) |
IMonnifyBillsClient.ValidateCustomerAsync |
POST /api/v1/vas/bills-payment/validate-customer |
Implemented | validationReference lives inside vendInstruction, not top-level as our docs prose implies. minAmount/maxAmount can appear top-level for amount-constrained products |
IMonnifyBillsClient.VendAsync |
POST /api/v1/vas/bills-payment/vend |
Implemented | Automatic retry disabled, same reasoning as Disbursements |
IMonnifyBillsClient.RequeryAsync |
GET /api/v1/vas/bills-payment/requery?reference= |
Implemented | Same response shape as VendAsync |
Update this table whenever a method moves from Planned to Implemented, or its endpoint changes.