Overview
The HitchPay API is a RESTful interface for building multi-currency accounts, wallets, USD cards, FX, payments and cross-border payouts across African markets and global corridors. All requests and responses use JSON.
Sandbox https://sandbox.hitchpay.ng
Production https://api.hitchpay.ng
All endpoints are versioned under /v1/. Authenticate with OAuth 2.0 (client credentials) and send the Bearer token on every request.
Responses & errors
Every response uses a consistent envelope. Successful calls return status: "success" with a data object; errors return status: "error" with a code and message.
{ "status": "success", "message": "...", "data": { } }
{ "status": "error", "code": "INVALID_REQUEST", "message": "..." }
| Code | Meaning |
INVALID_REQUEST | Malformed or missing parameters (400) |
INVALID_TOKEN | Missing, expired or invalid access token (401) |
KYC_REQUIRED | Customer must complete KYC first (403) |
DUPLICATE_REQUEST | A request with this reference already exists (409) |
Pass a unique reference on write operations for idempotency. Standard HTTP status codes apply (200, 201, 400, 401, 403, 404, 409, 500).
Authentication
OAuth 2.0 Client Credentials grant. Tokens are valid for one hour.
POST/v1/oauth/token
Generate access token
Authenticate with the OAuth 2.0 Client Credentials grant using your client_id and client_secret from the HitchPay Dashboard. The returned Bearer token is valid for 3600 seconds (1 hour).
Headers
Content-Type: application/x-www-form-urlencoded
Request body
{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"grant_type": "client_credentials"
}
Response
{
"status": "success",
"message": "Access token generated successfully",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
POST/v1/oauth/token
Refresh access token
Exchange a valid refresh_token for a new access token without re-sending your credentials.
Headers
Content-Type: application/x-www-form-urlencoded
Request body
{
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Response
{
"status": "success",
"message": "Access token generated successfully",
"data": {
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOi..."
}
}
Wallet
Balances across your currency wallets.
GET/v1/wallet/balance
Get wallet balance
Retrieve available and ledger balances across all your currency wallets.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Response
{
"status": "success",
"message": "Wallet balance retrieved successfully.",
"data": {
"balances": [
{ "currency": "NGN", "available_balance": 38646.97, "ledger_balance": 79975.00 },
{ "currency": "USD", "available_balance": 2000.00, "ledger_balance": 0.00 },
{ "currency": "KES", "available_balance": 15000.00, "ledger_balance": 15000.00 },
{ "currency": "GHS", "available_balance": 500.00, "ledger_balance": 500.00 }
]
}
}
Payments
Accept payments via hosted checkout and virtual accounts.
POST/v1/payments/initiate
Initiate payment
Start a payment and receive a hosted checkout URL to redirect your customer to. Pass a unique reference for idempotency.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Request body
{
"amount": 100,
"currency": "USD",
"reference": "REF_001",
"callback_url": "https://yourapp.com/callback",
"allow_currency_selection": true,
"customer": {
"name": "John Doe",
"email": "customer@example.com",
"phone": "08012345678"
}
}
Response
{
"status": "success",
"message": "Payment initiated successfully.",
"data": {
"reference": "HCHK248EBF3863",
"payment_url": "https://checkout.hitchpay.ng/pay/HCHK248EBF3863"
}
}
GET/v1/payments/verify/{reference}
Verify payment
Confirm the final status of a payment using its reference.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Response
{
"status": "success",
"message": "Payment verified",
"data": {
"status": "successful",
"amount": "100.00",
"currency": "NGN",
"amount_paid": "100.50",
"reference": "HCHK380B3716D4",
"paid_at": "2025-11-06T01:59:23.000Z",
"payment_channel": "dynamic_account",
"customer": { "name": "Jane Doe", "email": "jane@example.com" }
}
}
POST/v1/payments/dynamic-account
Create dynamic account
Generate a temporary virtual bank account for a single payment. The account is valid for 15 minutes unless payment is received.
Headers
Authorization: Bearer {access_token}
Content-Type: application/json
Request body
{
"reference": "1232456789",
"amount": 1000,
"currency": "NGN",
"description": "Testing"
}
Response
{
"status": "success",
"message": "Dynamic account generated. Awaiting payment.",
"data": {
"tracking_id": "EXTPAYA2165E437665",
"merchant_reference": "1232456789",
"account_number": "6020006173",
"account_name": "HitchAfricaTech Checkout",
"amount": 1000,
"currency": "NGN",
"valid_for": 900,
"expires_in": "15 minutes",
"status": "pending"
}
}
Banking
Issue virtual NGN, USD, GBP & EUR accounts for your customers.
POST/v1/customer/generate-account
Create static account
Generate a permanent, reusable virtual account number for a KYC-verified customer.
Headers
Authorization: Bearer {access_token}
Content-Type: application/json
Request body
{
"customer_id": "01113ca3-d86e-4812-ace7-69fe7d72fb4f",
"bank_type": "dotbank"
}
Response
{
"status": "success",
"message": "Account generated successfully.",
"data": {
"tracking_id": "EXTACCT75718C8F163A",
"account_number": "1000164160",
"account_name": "Olajide Olamide",
"bank_name": "Dot MFB",
"bank_code": "50162",
"provider": "dotbank",
"status": "active",
"date_created": "1776705788"
}
}
Customer & KYC
Enroll customers and complete identity verification.
POST/v1/customer/enroll
Enroll customer
Create a customer record. Customers start with a pending_kyc status until KYC is submitted and approved.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Request body
{
"customerid": "CUST_001",
"email": "olajide@example.com",
"phone": "07068363556",
"firstname": "Olajide",
"lastname": "Olamide",
"dob": "12-04-1990",
"nationality": "NG",
"image": "https://storage.yourapp.com/customers/profile-abc123.jpg",
"address": {
"house_no": 2,
"street": "12 Victoria Island Road",
"city": "Lagos",
"state": "Lagos",
"postal_code": "100001",
"country": "NG"
}
}
Response
{
"status": "success",
"message": "Customer created successfully.",
"data": {
"uuid": "42feffe9-d625-412e-bfa5-2f3089a11735",
"customerid": "CUST_001",
"firstname": "Olajide",
"lastname": "Olamide",
"email": "olajide@example.com",
"phone": "07068363556",
"created_at": "1773591858",
"kyc_status": "pending_kyc"
}
}
POST/v1/customer/{uuid}/kyc
Submit KYC
Submit identity documents for a customer. Verification runs asynchronously; the result is delivered via the kyc.approved / kyc.rejected webhooks.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Request body
{
"type": "drivers_license",
"country": "NG",
"document_front": "https://storage.yourapp.com/kyc/id-front-abc123.jpg",
"document_back": "https://storage.yourapp.com/kyc/id-back-abc123.jpg"
}
Response
{
"status": "success",
"message": "KYC information submitted successfully. Verification is in progress.",
"data": {
"uuid": "42feffe9-d625-412e-bfa5-2f3089a11735",
"customerid": "2912",
"kyc_status": "pending"
}
}
Global payments
Cross-border transfers and collections with live FX.
GET/v1/crossborder/rates
Get exchange rate
Fetch a live quote for a cross-border conversion.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Query parameters
| Param | Description |
|---|
source_currency | Currency being sent |
destination_currency | Currency the recipient receives |
amount | Amount in the source currency |
Response
{
"status": "success",
"message": "Rate retrieved successfully.",
"data": {
"source_currency": "USD",
"destination_currency": "NGN",
"source_amount": 10,
"destination_amount": 16000,
"exchange_rate": 1600.0,
"fee": 0.5,
"fee_currency": "USD"
}
}
POST/v1/crossborder/global-transfer
Initiate transfer
Send a cross-border payout to a bank account or mobile-money wallet. Use GET /crossborder/channels and /networks to resolve channel_id and network_id.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Request body
{
"customer_uuid": "dda04ec3-3774-4750-ae7b-0c070e8d084b",
"amount": 100,
"source_currency": "USD",
"destination_currency": "KES",
"reference": "REF_BANK_001",
"reason": "School fees",
"beneficiary": {
"account_name": "Jane Doe",
"account_number": "0123456789",
"network_id": "30f11b2e-0905-4c09-ba5f-d05aa4e4a11c",
"channel_id": "de274d59-46b3-49a8-8836-3c0cc5b73a4f",
"account_type": "bank",
"country_code": "KE"
}
}
Response
{
"status": "success",
"message": "Cross-border transfer initiated successfully.",
"data": {
"reference": "HTCB13BE4DAC468B",
"external_reference": "REF_BANK_001",
"status": "processing",
"provider_status": "created",
"provider_reference": "81939272-9c53-5671-8dd3-0a445ef19c48",
"amount_sent": 100,
"source_currency": "USD",
"estimated_amount_received": 12873.28,
"destination_currency": "KES"
}
}
GET/v1/crossborder/global-transfer/{reference}
Get transfer status
Poll the status of a cross-border transfer.
Headers
Authorization: Bearer {ACCESS_TOKEN}
Response
{
"status": "success",
"message": "Transfer status retrieved successfully.",
"data": {
"reference": "HTCBE5C7CAE5A463",
"external_reference": "REF_BANK_001",
"status": "processing",
"amount_sent": 100,
"source_currency": "USD",
"estimated_amount_received": 12873.28,
"destination_currency": "KES",
"failure_reason": null,
"beneficiary": { "account_name": "Jane Doe", "country_code": "KE", "account_type": "bank" }
}
}
POST/v1/crossborder/global-collection
Initiate collection
Collect funds cross-border from a payer via bank or mobile money.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Request body
{
"customer_uuid": "dda04ec3-3774-4750-ae7b-0c070e8d084b",
"amount": 5000,
"currency": "KES",
"channel_id": "de274d59-46b3-49a8-8836-3c0cc5b73a4f",
"account_number": "254722123456",
"network_id": "30f11b2e-0905-4c09-ba5f-d05aa4e4a11c",
"reason": "entertainment",
"reference": "REF7292901912",
"dial_code": "+254",
"account_type": "bank",
"country_code": "KE"
}
Response
{
"status": "success",
"message": "Cross-border collection initiated successfully.",
"data": {
"reference": "HTCCBEF4EC275285",
"external_reference": "REF7292901912",
"status": "processing",
"amount": 5000,
"currency": "KES",
"account_type": "bank",
"bankInfo": {
"accountName": "HitchPay Collections",
"accountNumber": "1234567890",
"bankName": "National Bank of Kenya",
"bankCode": "52"
}
}
}
GET/v1/crossborder/channels
Channels & networks
List supported payout channels, then GET /v1/crossborder/networks for the banks and mobile-money operators on each channel. Use the returned channel_id and network_id when initiating transfers and collections.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Response
{
"status": "success",
"message": "Channels retrieved successfully.",
"data": [ { "channel_id": "de274d59-...", "name": "Bank", "country_code": "KE" } ]
}
Webhooks
HitchPay sends real-time HTTP callbacks to your endpoint for asynchronous events. Every request includes an x-hitchpay-signature header — an HMAC SHA256 digest of the raw request body, keyed with your webhook_secret.
Verifying a signature
Compute an HMAC SHA256 of the raw request body, prefix it with sha256=, and compare it to the header. Always use the unparsed body (req.rawBody).
const crypto = require('crypto');\n\nfunction verifyWebhook(req, webhookSecret) {\n const signature = req.headers['x-hitchpay-signature'];\n const computed = 'sha256=' + crypto\n .createHmac('sha256', webhookSecret)\n .update(req.rawBody)\n .digest('hex');\n return computed === signature;\n}