Initiate Payout
Creates a payout/disbursement request from the merchant wallet or payout deposit balance.
/api/v1/payouts/external/initiateMerchant HMACPayout requests can move real funds. Confirm merchant enablement and business approval before sending an instruction. This page does not execute requests.
Authentication
Use merchant HMAC headers and sign the exact path, query and raw body.
See authentication.
Workflow notes
- Idempotency-Key must be at least 16 characters long.
- If the same Idempotency-Key is replayed for the same merchant, ZytePe returns the original payout transaction instead of creating a new one.
Before you send
Check the field labels and use your enabled merchant account. Examples illustrate the API contract; confirm deployment-specific limits and error handling before launch. This page never sends a request.
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 path = `/api/v1/payouts/external/initiate`;
const query = '';
const payload = {
"payee_id": "d965aa45-1952-4749-aa52-2bed21470ffd",
"amount": 1500,
"currency": "INR",
"external_ref": "PAYOUT-2026-00041",
"payout_method": "IMPS",
"purpose": "Vendor settlement",
"custom_notes": {
"invoice_no": "INV-882",
"batch": "MAY-SETTLEMENT"
}
};
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,
});
// Save this key before sending; reuse it for the same payment retry.
const idempotencyKey = process.env.ZYTEPE_IDEMPOTENCY_KEY;
if (!idempotencyKey || idempotencyKey.length < 16) throw new Error('Set a saved idempotency key of at least 16 characters');
headers['Idempotency-Key'] = idempotencyKey;
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
path = f'/api/v1/payouts/external/initiate'
query = ''
payload = json.loads('''{
"payee_id": "d965aa45-1952-4749-aa52-2bed21470ffd",
"amount": 1500,
"currency": "INR",
"external_ref": "PAYOUT-2026-00041",
"payout_method": "IMPS",
"purpose": "Vendor settlement",
"custom_notes": {
"invoice_no": "INV-882",
"batch": "MAY-SETTLEMENT"
}
}''')
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'],
)
# Reuse the saved key for the same payment retry.
key = os.environ['ZYTEPE_IDEMPOTENCY_KEY']
if len(key) < 16: raise ValueError('Idempotency key must be at least 16 characters')
headers['Idempotency-Key'] = key
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.
# A timeout does not mean failure. Recover before creating another payment.<?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/payouts/external/initiate';
$query = '';
$body = <<<'JSON'
{
"payee_id": "d965aa45-1952-4749-aa52-2bed21470ffd",
"amount": 1500,
"currency": "INR",
"external_ref": "PAYOUT-2026-00041",
"payout_method": "IMPS",
"purpose": "Vendor settlement",
"custom_notes": {
"invoice_no": "INV-882",
"batch": "MAY-SETTLEMENT"
}
}
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"];
// Save and reuse the same key for retries of this payment.
$retryKey = envRequired('ZYTEPE_IDEMPOTENCY_KEY');
if (strlen($retryKey) < 16) throw new RuntimeException('Idempotency key must be at least 16 characters');
$headers[] = 'Idempotency-Key: ' . $retryKey;
$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/payouts/external/initiate'
query = ''
body = <<~'JSON'.chomp
{
"payee_id": "d965aa45-1952-4749-aa52-2bed21470ffd",
"amount": 1500,
"currency": "INR",
"external_ref": "PAYOUT-2026-00041",
"payout_method": "IMPS",
"purpose": "Vendor settlement",
"custom_notes": {
"invoice_no": "INV-882",
"batch": "MAY-SETTLEMENT"
}
}
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 }
# Save and reuse this key for retries of the same payment.
retry_key = ENV.fetch('ZYTEPE_IDEMPOTENCY_KEY')
raise 'Idempotency key must be at least 16 characters' if retry_key.length < 16
headers['Idempotency-Key'] = retry_key
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/payouts/external/initiate";
String query = "";
String body = """
{
"payee_id": "d965aa45-1952-4749-aa52-2bed21470ffd",
"amount": 1500,
"currency": "INR",
"external_ref": "PAYOUT-2026-00041",
"payout_method": "IMPS",
"purpose": "Vendor settlement",
"custom_notes": {
"invoice_no": "INV-882",
"batch": "MAY-SETTLEMENT"
}
}
""".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);
// Save this key before sending; reuse it for retries of the same payment.
String retryKey = env("ZYTEPE_IDEMPOTENCY_KEY");
if (retryKey.length() < 16) throw new IllegalArgumentException("Idempotency key must be at least 16 characters");
request.header("Idempotency-Key", retryKey);
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/payouts/external/initiate' \
--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 'Idempotency-Key: <saved-key-at-least-16-characters>' \
--header 'Content-Type: application/json' \
--data-binary @request.jsonRequest fields
Optional fields can be omitted. Conditional fields depend on the payment method. Labels reflect the reviewed API schemas and handler checks.
payee_idRequiredThe saved and verified payee identifier.
amountRequiredPayment amount in the documented currency.
external_refRequiredYour unique business payout reference.
currencyOptionalDefaults to INR.
payout_methodOptionalDefaults to IMPS. Verify the payee for your chosen method.
purposeOptionalBusiness reason for the payout.
custom_notesOptionalAdditional business context.
Path, query and headers
Idempotency-KeyRequiredheaderRequired. Reuse the same key for retries of the same payout.
Request body
{
"payee_id": "d965aa45-1952-4749-aa52-2bed21470ffd",
"amount": 1500,
"currency": "INR",
"external_ref": "PAYOUT-2026-00041",
"payout_method": "IMPS",
"purpose": "Vendor settlement",
"custom_notes": {
"invoice_no": "INV-882",
"batch": "MAY-SETTLEMENT"
}
}Example response
{
"success": true,
"message": "Payout initiated successfully.",
"data": {
"message": "Payout initiated successfully.",
"transaction_id": "ca9a58aa-d578-4fc2-a9f4-fb8d090d4b5f",
"status": "processing"
}
}