MENU navbar-image

Introduction

Getting Started With Your API

This guide helps you get started with the Payment System API integration.

API Base URL

We provide separate base URLs for two environments:

Recommended Steps:

  1. Start with the UAT base URL to explore the API and test requests.
  2. Once your integration works as expected, switch to the Production base URL.

Key Features

🔐 Authentication

All endpoints require authentication using two credentials:

Every payout and balance request is validated in this order:

  1. API key and secret match a merchant account.
  2. Merchant status must be active.
  3. kyc_status and van_status must be verified.
  4. Merchant callback/IP activation must be verified, with webhook URL/secret set.
  5. Caller IP must match the merchant's whitelisted IP.

If any validation fails, the API returns an error response in the same response envelope format.

🔒 Encryption

All sensitive request data is encrypted using AES-256-CBC.

Ensure your client application implements the same encryption and decryption logic before sending the payload. The API expects the payload to be base64 encoded after encryption.

Example workflow:

  1. Prepare your request data as JSON.
  2. Encrypt using AES-256-CBC with your api_secret and the default IV.
  3. Base64 encode the encrypted string.
  4. Send it in the request body or as defined by the endpoint.

💡 Tip for Developers

Use the provided code examples (on the right in desktop view or below on mobile) to quickly test requests in your preferred programming language.
Following the examples ensures correct headers, encryption, and payload structure.


Webhook Integration Guide

When a payout status changes (e.g., processingsuccess or failed), the system automatically sends a POST request to your registered Webhook URL.
You must implement this endpoint on your server to receive real-time payout status updates.

How It Works

  1. You register a Webhook URL and a Webhook Secret during IP/callback activation.
  2. When a payout status changes, the system encrypts the payload and POSTs it to your URL.
  3. Your endpoint must respond with HTTP 200 to acknowledge receipt.
  4. If your server does not return 200, the system will retry up to 5 times at the following intervals:
    • 1 minute, 5 minutes, 15 minutes (×3 remaining retries)

Incoming Request Format

Your webhook URL will receive a POST request with:

Header Value
Content-Type application/json
x-signature Your registered Webhook Secret

The raw request body is an AES-256-CBC encrypted, Base64-encoded string — not a JSON object.

Decrypting the Payload

To read the webhook data, decrypt the raw body using:

After decryption, you get a JSON string with the following fields:

{
  "transaction_id": "TXN987654321",
  "beneficiary_account_holder": "John Doe",
  "beneficiary_account_number": "1234567890",
  "beneficiary_bank_name": "HDFC Bank",
  "beneficiary_ifsc_code": "HDFC0001234",
  "amount": 500,
  "status": "success",
  "utr": "UTR1234567890",
  "remarks": "Payment for services",
  "narration": "Salary"
}

Payload Field Reference

Field Type Description
transaction_id string Your unique payout transaction ID
beneficiary_account_holder string Beneficiary's full name
beneficiary_account_number string Beneficiary's bank account number
beneficiary_bank_name string Beneficiary's bank name
beneficiary_ifsc_code string Beneficiary's bank IFSC code
amount number Payout amount in INR
status string Current payout status: pending, processing, success, failed
utr string|null UTR number (available on success)
remarks string|null Remarks for the payout
narration string|null Narration for the transaction

Before processing, verify that the request is genuinely from our system by checking the x-signature header against your registered Webhook Secret.

Implementation Examples

PHP

<?php

// Webhook endpoint: POST https://your-domain.com/webhook/payout

$api_secret  = 'YOUR_API_SECRET';   // same as your api_secret
$webhook_secret = 'YOUR_WEBHOOK_SECRET';
$iv = '0g7H#8X2mTqjvLwR';

// 1. Verify signature
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
if ($signature !== $webhook_secret) {
    http_response_code(401);
    exit('Unauthorized');
}

// 2. Read and decrypt body
$raw_body  = file_get_contents('php://input');
$decrypted = openssl_decrypt(
    base64_decode($raw_body),
    'AES-256-CBC',
    $api_secret,
    OPENSSL_RAW_DATA,
    $iv
);

$payload = json_decode($decrypted, true);

// 3. Process the payload
$transaction_id = $payload['transaction_id'];
$status         = $payload['status'];
$utr            = $payload['utr'] ?? null;

// ... update your database, notify user, etc.

// 4. MUST return HTTP 200
http_response_code(200);
echo json_encode(['received' => true]);

Node.js (Express)

const express = require('express');
const crypto  = require('crypto');
const app     = express();

const API_SECRET     = 'YOUR_API_SECRET';
const WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET';
const IV             = '0g7H#8X2mTqjvLwR';

app.post('/webhook/payout', express.text({ type: '*/*' }), (req, res) => {
  // 1. Verify signature
  if (req.headers['x-signature'] !== WEBHOOK_SECRET) {
    return res.status(401).send('Unauthorized');
  }

  // 2. Decrypt body
  const decipher = crypto.createDecipheriv(
    'aes-256-cbc',
    Buffer.from(API_SECRET),
    Buffer.from(IV)
  );
  let decrypted = decipher.update(Buffer.from(req.body, 'base64'));
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  const payload = JSON.parse(decrypted.toString());

  // 3. Process
  console.log('Payout update:', payload.transaction_id, payload.status);

  // 4. MUST return HTTP 200
  res.status(200).json({ received: true });
});

⚠️ Important: Your webhook endpoint must return HTTP 200 within the timeout window.
Any other status code (including 201, 204, 4xx, 5xx) will be treated as a failure and trigger a retry.

Authenticating requests

To authenticate requests, include a X-API-KEY header with the value "{YOUR_API_KEY}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Also send X-API-SECRET in headers for decryption/encryption and merchant validation.

Account

Get account balance.

Retrieve the current balance of the authenticated merchant's virtual account.

Note:

Example request:
curl --request GET \
    --get "https://uat.makemypayment.in/api/v1/balance" \
    --header "X-API-KEY: string required Your API key for authentication." \
    --header "X-API-SECRET: string required Your API secret for encryption and authentication." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://uat.makemypayment.in/api/v1/balance"
);

const headers = {
    "X-API-KEY": "string required Your API key for authentication.",
    "X-API-SECRET": "string required Your API secret for encryption and authentication.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://uat.makemypayment.in/api/v1/balance';
$response = $client->get(
    $url,
    [
        'headers' => [
            'X-API-KEY' => 'string required Your API key for authentication.',
            'X-API-SECRET' => 'string required Your API secret for encryption and authentication.',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://uat.makemypayment.in/api/v1/balance'
headers = {
  'X-API-KEY': 'string required Your API key for authentication.',
  'X-API-SECRET': 'string required Your API secret for encryption and authentication.',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "status": true,
    "message": "Account balance retrieved successfully",
    "data": {
        "balance": 15000.5
    },
    "errors": null
}
 

Example response (400):


{
    "status": false,
    "message": "API key and secret are required",
    "data": null,
    "errors": []
}
 

Example response (401):


{
    "status": false,
    "message": "Invalid API key or secret",
    "data": null,
    "errors": []
}
 

Example response (403):


{
    "status": false,
    "message": "Merchant virtual account is not verified.",
    "data": null,
    "errors": []
}
 

Request      

GET api/v1/balance

Headers

X-API-KEY        

Example: string required Your API key for authentication.

X-API-SECRET        

Example: string required Your API secret for encryption and authentication.

Content-Type        

Example: application/json

Accept        

Example: application/json

Payouts

Initiate a payout.

Start a new payout transaction for the authenticated merchant.

Note: The request body must be:

Outbound Webhook (Status Notification):

When the payout status changes (e.g., processing, success, failed), the system will automatically POST an AES-256-CBC encrypted, Base64-encoded payload to your registered webhook URL.

The raw JSON payload before encryption contains:

{
  "transaction_id": "TXN987654321",
  "beneficiary_account_holder": "John Doe",
  "beneficiary_account_number": "1234567890",
  "beneficiary_bank_name": "HDFC Bank",
  "beneficiary_ifsc_code": "HDFC0001234",
  "amount": 500,
  "status": "success",
  "utr": "UTR1234567890",
  "remarks": "Payment for services",
  "narration": "Salary"
}

The encrypted string is sent as the raw POST body with headers:

Your webhook endpoint MUST respond with HTTP 200 to acknowledge receipt. Any other response code is treated as failure and the system will retry up to 5 times (at 1 min, 5 min, and 15 min intervals).

To decrypt the webhook payload, use AES-256-CBC with your api_secret as the key and the fixed IV 0g7H#8X2mTqjvLwR, then Base64-decode the body first.

Example request:
curl --request POST \
    "https://uat.makemypayment.in/api/v1/payouts/initiate" \
    --header "X-API-KEY: string required Your API key for authentication." \
    --header "X-API-SECRET: string required Your API secret for decryption and authentication." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"account_holder\": \"John Doe\",
    \"account_number\": \"1234567890\",
    \"ifsc_code\": \"HDFC0001234\",
    \"bank_name\": \"HDFC Bank\",
    \"branch_name\": \"architecto\",
    \"branch_code\": \"architecto\",
    \"mobile\": \"9876543210\",
    \"city\": \"architecto\",
    \"beneficiary_address\": \"architecto\",
    \"amount\": \"500\",
    \"mode\": \"neft\",
    \"purpose\": \"architecto\",
    \"email\": \"user@example.com\",
    \"state\": \"bngzmi\",
    \"pincode\": \"569775\",
    \"remarks\": \"architecto\",
    \"narration\": \"architecto\"
}"
const url = new URL(
    "https://uat.makemypayment.in/api/v1/payouts/initiate"
);

const headers = {
    "X-API-KEY": "string required Your API key for authentication.",
    "X-API-SECRET": "string required Your API secret for decryption and authentication.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "account_holder": "John Doe",
    "account_number": "1234567890",
    "ifsc_code": "HDFC0001234",
    "bank_name": "HDFC Bank",
    "branch_name": "architecto",
    "branch_code": "architecto",
    "mobile": "9876543210",
    "city": "architecto",
    "beneficiary_address": "architecto",
    "amount": "500",
    "mode": "neft",
    "purpose": "architecto",
    "email": "user@example.com",
    "state": "bngzmi",
    "pincode": "569775",
    "remarks": "architecto",
    "narration": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://uat.makemypayment.in/api/v1/payouts/initiate';
$response = $client->post(
    $url,
    [
        'headers' => [
            'X-API-KEY' => 'string required Your API key for authentication.',
            'X-API-SECRET' => 'string required Your API secret for decryption and authentication.',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'account_holder' => 'John Doe',
            'account_number' => '1234567890',
            'ifsc_code' => 'HDFC0001234',
            'bank_name' => 'HDFC Bank',
            'branch_name' => 'architecto',
            'branch_code' => 'architecto',
            'mobile' => '9876543210',
            'city' => 'architecto',
            'beneficiary_address' => 'architecto',
            'amount' => '500',
            'mode' => 'neft',
            'purpose' => 'architecto',
            'email' => 'user@example.com',
            'state' => 'bngzmi',
            'pincode' => '569775',
            'remarks' => 'architecto',
            'narration' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://uat.makemypayment.in/api/v1/payouts/initiate'
payload = {
    "account_holder": "John Doe",
    "account_number": "1234567890",
    "ifsc_code": "HDFC0001234",
    "bank_name": "HDFC Bank",
    "branch_name": "architecto",
    "branch_code": "architecto",
    "mobile": "9876543210",
    "city": "architecto",
    "beneficiary_address": "architecto",
    "amount": "500",
    "mode": "neft",
    "purpose": "architecto",
    "email": "user@example.com",
    "state": "bngzmi",
    "pincode": "569775",
    "remarks": "architecto",
    "narration": "architecto"
}
headers = {
  'X-API-KEY': 'string required Your API key for authentication.',
  'X-API-SECRET': 'string required Your API secret for decryption and authentication.',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "status": true,
    "message": "Payout initiated successfully",
    "data": "TXN987654321",
    "errors": null
}
 

Example response (400):


{
    "status": false,
    "message": "API key and secret are required",
    "data": null,
    "errors": []
}
 

Example response (400):


{
    "status": false,
    "message": "Request body is not valid base64 encoded",
    "data": null,
    "errors": []
}
 

Example response (400):


{
    "status": false,
    "message": "Failed to decrypt data",
    "data": null,
    "errors": []
}
 

Example response (400):


{
    "status": false,
    "message": "Invalid JSON format after decryption",
    "data": null,
    "errors": []
}
 

Example response (401):


{
    "status": false,
    "message": "Invalid API key or secret",
    "data": null,
    "errors": []
}
 

Example response (403):


{
    "status": false,
    "message": "Merchant KYC is not verified.",
    "data": null,
    "errors": []
}
 

Example response (422):


{
    "status": false,
    "message": "Validation failed",
    "data": null,
    "errors": {
        "account_number": [
            "The account number field is required."
        ],
        "amount": [
            "The amount must be at least merchant min transfer limit."
        ]
    }
}
 

Example response (500):


{
    "status": false,
    "message": "Failed to initiate payout. Please try again.",
    "data": null,
    "errors": []
}
 

Request      

POST api/v1/payouts/initiate

Headers

X-API-KEY        

Example: string required Your API key for authentication.

X-API-SECRET        

Example: string required Your API secret for decryption and authentication.

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

account_holder   string     

The full name of the bank account holder. Example: John Doe

account_number   string     

The bank account number. Example: 1234567890

ifsc_code   string     

The IFSC code of the bank branch. Example: HDFC0001234

bank_name   string     

The name of the bank. Example: HDFC Bank

branch_name   string     

Example: architecto

branch_code   string     

Example: architecto

mobile   numeric     

The mobile number of the beneficiary (10 digits). Example: 9876543210

city   string     

Example: architecto

beneficiary_address   string     

Example: architecto

amount   numeric     

The payout amount (must be at least 100). Example: 500

mode   string     

Example: neft

Must be one of:
  • imps
  • neft
  • rtgs
  • a2a
purpose   string     

Example: architecto

email   string     

The email address of the beneficiary. Example: user@example.com

state   string  optional    

Must contain only letters. Example: bngzmi

pincode   string  optional    

Must be 6 digits. Example: 569775

remarks   string  optional    

Example: architecto

narration   string  optional    

Example: architecto

Initiate bulk payouts.

Accepts an array of payout objects, persists each as 'initiated', and dispatches async jobs to process them via SprintNXT.

Note: AES encrypted request / response same as initiate payout. Merchant pre-validation is enforced before request processing.

Example request:
curl --request POST \
    "https://uat.makemypayment.in/api/v1/payouts/bulk" \
    --header "X-API-KEY: string required" \
    --header "X-API-SECRET: string required" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"payouts\": [
        \"architecto\"
    ]
}"
const url = new URL(
    "https://uat.makemypayment.in/api/v1/payouts/bulk"
);

const headers = {
    "X-API-KEY": "string required",
    "X-API-SECRET": "string required",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "payouts": [
        "architecto"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://uat.makemypayment.in/api/v1/payouts/bulk';
$response = $client->post(
    $url,
    [
        'headers' => [
            'X-API-KEY' => 'string required',
            'X-API-SECRET' => 'string required',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'payouts' => [
                'architecto',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://uat.makemypayment.in/api/v1/payouts/bulk'
payload = {
    "payouts": [
        "architecto"
    ]
}
headers = {
  'X-API-KEY': 'string required',
  'X-API-SECRET': 'string required',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "status": true,
    "message": "Bulk payout initiated",
    "data": {
        "transaction_ids": [
            "ABCDEF1234567890"
        ]
    },
    "errors": null
}
 

Example response (403):


{
    "status": false,
    "message": "Merchant API activation is not verified.",
    "data": null,
    "errors": []
}
 

Request      

POST api/v1/payouts/bulk

Headers

X-API-KEY        

Example: string required

X-API-SECRET        

Example: string required

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

payouts   string[]     

Array of payout objects. Each item has the same fields as initiate payout.

account_holder   string     

Must not be greater than 255 characters. Example: b

account_number   string     

Example: architecto

ifsc_code   string     

Example: architecto

bank_name   string     

Example: architecto

branch_name   string     

Example: architecto

branch_code   string     

Example: architecto

mobile   string     

Must be 10 digits. Example: 8225697751

city   string     

Example: architecto

beneficiary_address   string     

Example: architecto

amount   string  optional    
mode   string     

Example: neft

Must be one of:
  • imps
  • neft
  • rtgs
  • a2a
purpose   string     

Example: architecto

email   string  optional    

Must be a valid email address. Example: zbailey@example.net

state   string  optional    

Must contain only letters. Example: bngzmi

pincode   string  optional    

Must be 6 digits. Example: 569775

remarks   string  optional    

Example: architecto

narration   string  optional    

Example: architecto

Check payout status.

Get the status of a specific payout transaction for the authenticated merchant.

Note:

Example request:
curl --request GET \
    --get "https://uat.makemypayment.in/api/v1/payouts/status" \
    --header "X-API-KEY: string required Your API key for authentication." \
    --header "X-API-SECRET: string required Your API secret for encryption and authentication." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"transaction_id\": \"b\"
}"
const url = new URL(
    "https://uat.makemypayment.in/api/v1/payouts/status"
);

const headers = {
    "X-API-KEY": "string required Your API key for authentication.",
    "X-API-SECRET": "string required Your API secret for encryption and authentication.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "transaction_id": "b"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://uat.makemypayment.in/api/v1/payouts/status';
$response = $client->get(
    $url,
    [
        'headers' => [
            'X-API-KEY' => 'string required Your API key for authentication.',
            'X-API-SECRET' => 'string required Your API secret for encryption and authentication.',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'transaction_id' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'https://uat.makemypayment.in/api/v1/payouts/status'
payload = {
    "transaction_id": "b"
}
headers = {
  'X-API-KEY': 'string required Your API key for authentication.',
  'X-API-SECRET': 'string required Your API secret for encryption and authentication.',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "status": true,
    "message": "Payout status retrieved successfully",
    "data": {
        "transaction_id": "TXN123456",
        "beneficiary": {
            "account_holder": "John Doe",
            "account_number": "1234567890",
            "bank_name": "HDFC Bank",
            "ifsc_code": "HDFC0001234"
        },
        "amount": "1000.00",
        "status": "success",
        "utr": "UTR123456789",
        "remarks": "Payment processed successfully"
    },
    "errors": null
}
 

Example response (400):


{
    "status": false,
    "message": "API key and secret are required",
    "data": null,
    "errors": []
}
 

Example response (401):


{
    "status": false,
    "message": "Invalid API key or secret",
    "data": null,
    "errors": []
}
 

Example response (403):


{
    "status": false,
    "message": "Request IP is not whitelisted for this merchant.",
    "data": null,
    "errors": []
}
 

Example response (404):


{
    "status": false,
    "message": "Payout not found",
    "data": null,
    "errors": []
}
 

Request      

GET api/v1/payouts/status

Headers

X-API-KEY        

Example: string required Your API key for authentication.

X-API-SECRET        

Example: string required Your API secret for encryption and authentication.

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

transaction_id   string     

The transaction ID of the payout. Example: TXN123456

Body Parameters

transaction_id   string     

Must not be greater than 255 characters. Example: b