1. Custody Bank Deposit
Nilpay
  • NilPay Bank Integration API
    • Custody Bank Deposit
      • Integration Guide
      • Initiate deposit transaction
        POST
      • Complete deposit transaction
        POST
    • Bank Cashout
      • Integration Guide
      • Inquiry for bank cashout
      • Initiate bank cashout
      • Complete bank cashout with OTP
    • Schemas
      • InitiateDepositRequest
      • BankCashoutInquiryRequest
      • CompleteDepositRequest
      • BankCashoutInitiateRequest
      • BankCashoutCompleteRequest
      • Depositor
      • BankCashoutInquirySuccessResponse
      • InitiateDepositSuccessResponse
      • ChargeResponse
      • BankCashoutInitiateSuccessResponse
      • AccountBasicInfo
      • BankCashoutCompleteSuccessResponse
      • CompleteDepositSuccessResponse
      • WalletBasicInfo
      • ErrorResponse
      • ValidationErrorResponse
      • TransactionResponse
      • ProblemDetailsResponse
  • NilPay External Party Transfer API
    • Transactions
      • Integration Guide
      • External Party Transfer To Wallet
    • Schemas
      • ExternalPartyTransferRequest
      • TransactionResponseData
      • ExternalPartyTransferSuccessResponse
      • ErrorResponse
  1. Custody Bank Deposit

Integration Guide

1) Getting Started#

Overview#

This API allows partner banks to deposit money into NilPay wallets using a two-phase flow:
1.
POST /v1/transactions/custody-bank/deposit/initiate
2.
POST /v1/transactions/custody-bank/deposit/complete

Base URL#

https://api.dev.pynil.com

Version Prefix#

All routes start with /v1.

2) Authentication and Security#

All requests require:
Authorization: Basic <BASE64(username:password)>
X-Signature: <HMAC_SHA256_UPPERCASE_HEX>
X-Timestamp: <UNIX_TIMESTAMP_SECONDS>
Content-Type: application/json

HMAC Payload (Current Implementation)#

Use this exact payload format:
HTTP_METHOD + "\n" + REQUEST_PATH + "\n" + UNIX_TIMESTAMP
Example request path:
/v1/transactions/custody-bank/deposit/initiate

Security Rules#

Timestamp tolerance: 5 minutes.
Replay protection: request uniqueness is validated using timestamp + signature.
Signature comparison is case-insensitive, but use uppercase hex in clients.
HMAC secret is resolved from authenticated bank user (with optional system fallback).

3) Endpoint: Initiate Deposit#

URL#

POST /v1/transactions/custody-bank/deposit/initiate

Request Body#

{
  "walletNumber": "101000030",
  "amount": 500.0,
  "timestamp": "2025-08-03",
  "notes": "Layla deposits money"
}

Request Notes#

walletNumber, amount, timestamp are required.

Success Response (200)#

{
  "success": true,
  "message": "Success.",
  "statusCode": 200,
  "data": {
    "account": {
      "branchCode": "MAIN",
      "bban": "12000010000101000030",
      "currencyCode": "SDG",
      "walletName": "Layla ismaeil",
      "status": "Active"
    },
    "charges": [
      {
        "name": "Custody Deposit",
        "amount": 20.0
      }
    ],
    "totalFees": 20.0
  }
}

Error Responses#

400 validation response envelope (success/message/statusCode/data)
401 RFC 7807 ProblemDetails (e.g., missing signature, invalid timestamp, invalid signature)
404 envelope error (wallet/depositor/shadow not found)
500 envelope error

4) Endpoint: Complete Deposit#

URL#

POST /v1/transactions/custody-bank/deposit/complete

Request Body#

{
  "walletNumber": "101000030",
  "currency": "SDG",
  "amount": 500.0,
  "depositor": {
    "fullName": "Layla ismaeil",
    "mobile": "0918003874",
    "idNumber": "1001-1090-180",
    "address": "Khartoum - sudan"
  },
  "BankTranID": "41128",
  "timestamp": "2025-08-03",
  "notes": "Layla deposits money"
}

Request Notes#

depositor object is optional.
BankTranID is required and used for idempotency.

Idempotency Behavior#

If the same BankTranID was already processed successfully, API returns the existing nilpayTranRef.

Success Response (200)#

{
  "success": true,
  "message": "Success.",
  "statusCode": 200,
  "data": {
    "nilpayTranRef": 6
  }
}

Error Responses#

Same categories as initiate endpoint.

5) ProblemDetails Example (401)#

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid signature"
}

6) Error Codes#

HTTP CodeError TypeDescriptionAction Required
400Validation ErrorInvalid request parametersFix request payload and retry
401Authentication FailedBasic Auth, signature, timestamp, or replay validation failureVerify credentials, signature payload, and timestamp
404Account Not FoundWallet/depositor/shadow account not found or inactiveVerify wallet and configuration
500Internal Server ErrorUnrecoverable server errorRetry later, contact support if persistent

7) cURL Example (Initiate)#


8) cURL Example (Complete)#

Next
Initiate deposit transaction
Built with