Bank and UPI verification
Verify a saved payee for the payout method your workflow uses.
Verify a bank account
Create the payee first, then call Verify payee bank account with its resolved ID and an empty JSON object body. The source calls for bank verification before IMPS, NEFT or RTGS payouts.
Verify a UPI ID
For UPI payouts, call Verify payee UPI ID on the saved payee. It also uses merchant HMAC and an empty JSON object body. A separate raw-VPA dashboard helper is not part of this merchant API.
Interpret the result carefully
The response examples include verified and the account-holder name. A dummy/test account returning verified is not proof of real bank-provider validation. Confirm provider behavior and account eligibility before relying on verification in your production risk process.
Request example
Choose Node.js, Python, PHP, Ruby, Java or cURL. Run signed requests on your backend. PHP, Ruby and Java include signing directly. Java requires JDK 17 or newer.
Signing helpers: Node.js · Python. cURL shows the request shape; generate its signature first.
import { signedHeaders } from './signing.mjs';
const payee_id = process.env.ZYTEPE_PAYEE_ID;
if (!payee_id) throw new Error('Set ZYTEPE_PAYEE_ID to the returned identifier');
const path = `/api/v1/payees/external/${encodeURIComponent(payee_id)}/verify/bank`;
const query = '';
const payload = {};
const body = JSON.stringify(payload);
const headers = signedHeaders({
method: 'POST', path, query, body,
keyId: process.env.ZYTEPE_API_KEY_ID,
secret: process.env.ZYTEPE_API_SECRET,
});
headers['Content-Type'] = 'application/json';
const url = 'https://api.zytepe.com' + path + (query ? '?' + query : '');
const response = await fetch(url, { method: 'POST', headers, body });
if (!response.ok) throw new Error('Request failed: HTTP ' + response.status);
const result = await response.json();
// Store the returned identifiers securely. Do not log customer data.import json, os
from urllib.parse import quote
from urllib.request import Request, urlopen
from zytepe_signing import signed_headers
payee_id = os.environ['ZYTEPE_PAYEE_ID']
path = f'/api/v1/payees/external/{quote(payee_id, safe="")}/verify/bank'
query = ''
payload = json.loads('''{}''')
body = json.dumps(payload, separators=(',', ':'), ensure_ascii=False)
headers = signed_headers(
method='POST', path=path, query=query, body=body,
key_id=os.environ['ZYTEPE_API_KEY_ID'],
secret=os.environ['ZYTEPE_API_SECRET'],
)
headers['Content-Type'] = 'application/json'
url = 'https://api.zytepe.com' + path + ('?' + query if query else '')
request = Request(url, method='POST', headers=headers, data=body.encode('utf-8'))
with urlopen(request, timeout=30) as response:
result = json.load(response)
# Store identifiers securely; handle HTTPError/URLError in your application.<?php
// PHP 8+ with the cURL extension. Run on your server.
function envRequired(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') throw new RuntimeException('Set ' . $name);
return $value;
}
$path = '/api/v1/payees/external/{payee_id}/verify/bank';
$path = str_replace('{payee_id}', rawurlencode(envRequired('ZYTEPE_PAYEE_ID')), $path);
$query = '';
$body = <<<'JSON'
{}
JSON;
$headers = [];
$key = envRequired('ZYTEPE_API_KEY_ID');
$secret = envRequired('ZYTEPE_API_SECRET');
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = implode("\n", ['POST', $path, $query, $timestamp, $nonce, hash('sha256', $body)]);
$signature = hash_hmac('sha256', $canonical, $secret);
$headers = ["X-API-KEY-ID: $key", "X-API-TIMESTAMP: $timestamp", "X-API-NONCE: $nonce", "X-API-SIGNATURE: $signature"];
$headers[] = 'Content-Type: application/json';
$url = 'https://api.zytepe.com' . $path . ($query !== '' ? '?' . $query : '');
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => $body]);
$response = curl_exec($ch);
if ($response === false) throw new RuntimeException(curl_error($ch));
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) throw new RuntimeException('HTTP ' . $status);
$result = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
// Store returned identifiers securely. Recover uncertain payments before retrying.require 'net/http'
require 'uri'
require 'json'
require 'openssl'
require 'securerandom'
path = '/api/v1/payees/external/{payee_id}/verify/bank'
path = path.gsub('{payee_id}', URI.encode_www_form_component(ENV.fetch('ZYTEPE_PAYEE_ID')).gsub('+', '%20'))
query = ''
body = <<~'JSON'.chomp
{}
JSON
headers = {}
key = ENV.fetch('ZYTEPE_API_KEY_ID')
secret = ENV.fetch('ZYTEPE_API_SECRET')
timestamp = Time.now.to_i.to_s
nonce = SecureRandom.hex(16)
canonical = ['POST', path, query, timestamp, nonce, OpenSSL::Digest::SHA256.hexdigest(body)].join("\n")
signature = OpenSSL::HMAC.hexdigest('SHA256', secret, canonical)
headers = { 'X-API-KEY-ID' => key, 'X-API-TIMESTAMP' => timestamp,
'X-API-NONCE' => nonce, 'X-API-SIGNATURE' => signature }
headers['Content-Type'] = 'application/json'
uri = URI('https://api.zytepe.com' + path + (query.empty? ? '' : '?' + query))
request = Net::HTTP::Post.new(uri, headers)
request.body = body
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 30) do |http|
http.request(request)
end
raise 'HTTP ' + response.code unless response.is_a?(Net::HTTPSuccess)
result = JSON.parse(response.body)
# Store returned identifiers securely. Recover uncertain payments before retrying.// Java 17+. Save as ZytePeExample.java and run on your server.
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.security.MessageDigest;
import java.util.HexFormat;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class ZytePeExample {
private static String env(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) throw new IllegalArgumentException("Set " + name);
return value;
}
public static void main(String[] args) throws Exception {
String path = "/api/v1/payees/external/{payee_id}/verify/bank";
path = path.replace("{payee_id}", URLEncoder.encode(env("ZYTEPE_PAYEE_ID"), StandardCharsets.UTF_8).replace("+", "%20"));
String query = "";
String body = """
{}
""".stripTrailing();
var request = HttpRequest.newBuilder(URI.create(
"https://api.zytepe.com" + path + (query.isEmpty() ? "" : "?" + query)))
.timeout(Duration.ofSeconds(30));
String key = env("ZYTEPE_API_KEY_ID");
String secret = env("ZYTEPE_API_SECRET");
String timestamp = Long.toString(Instant.now().getEpochSecond());
String nonce = UUID.randomUUID().toString().replace("-", "");
String bodyHash = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256")
.digest(body.getBytes(StandardCharsets.UTF_8)));
String canonical = String.join("\n", "POST", path, query, timestamp, nonce, bodyHash);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
request.header("X-API-KEY-ID", key).header("X-API-TIMESTAMP", timestamp)
.header("X-API-NONCE", nonce).header("X-API-SIGNATURE", signature);
request.header("Content-Type", "application/json");
request.method("POST", HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8));
var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
var response = client.send(request.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (response.statusCode() < 200 || response.statusCode() >= 300)
throw new IllegalStateException("HTTP " + response.statusCode());
String result = response.body(); // Parse JSON using your application's JSON library.
// Store identifiers securely. Recover uncertain payments before retrying.
}
}curl --request POST \
--url 'https://api.zytepe.com/api/v1/payees/external/{payee_id}/verify/bank' \
--header 'X-API-KEY-ID: <key-id>' \
--header 'X-API-TIMESTAMP: <unix-seconds>' \
--header 'X-API-NONCE: <fresh-nonce>' \
--header 'X-API-SIGNATURE: <signature>' \
--header 'Content-Type: application/json' \
--data-binary @request.jsonPath, query and headers
payee_idRequiredpathUse the identifier returned by the corresponding creation operation.
Request body
{}Example response
{
"success": true,
"message": "Bank verification processed.",
"data": {
"status": "verified",
"name_at_bank": "Aditi Traders"
}
}