curl --request POST \
--url https://api.jenzy.com/v1/collections/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"amount_mwk": 50000,
"rail": "momo"
}
'import requests
url = "https://api.jenzy.com/v1/collections/simulate"
payload = {
"amount_mwk": 50000,
"rail": "momo"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({amount_mwk: 50000, rail: 'momo'})
};
fetch('https://api.jenzy.com/v1/collections/simulate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.jenzy.com/v1/collections/simulate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount_mwk' => 50000,
'rail' => 'momo'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.jenzy.com/v1/collections/simulate"
payload := strings.NewReader("{\n \"amount_mwk\": 50000,\n \"rail\": \"momo\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.jenzy.com/v1/collections/simulate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"amount_mwk\": 50000,\n \"rail\": \"momo\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.jenzy.com/v1/collections/simulate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount_mwk\": 50000,\n \"rail\": \"momo\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "settled",
"amount_mwk": "1000.00",
"fee": "15.00",
"amount_credited": "985.00",
"rail": "bank",
"source_customer_name": "PETER BANDA",
"source_account_number": "265991234567",
"source_institution": "Airtel Money",
"rtp_reference_number": "<string>",
"created_at": "2023-11-07T05:31:56Z"
}{
"status": "pending"
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}Simulate a pay-in (sandbox only)
Fund your sandbox balance by pretending a customer paid you. Sandbox only — in production this returns 403 sandbox_only, because real money can only arrive by being sent to your account number.
The simulated pay-in is a real pay-in in every way that matters to your integration: it becomes an ordinary collection, it credits your balance net of the fee, and it fires the collection.settled webhook. Nothing marks it as simulated, so the code you write against it is the code that runs in production.
This request is not idempotent. A 202 means the pay-in exists at the provider but has not credited yet — poll GET /collections rather than sending it again, or you will create a second pay-in. Only 4xx and 503 mean nothing was created.
Otherwise, repeat it as often as you like — each call is an independent collection.
curl --request POST \
--url https://api.jenzy.com/v1/collections/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"amount_mwk": 50000,
"rail": "momo"
}
'import requests
url = "https://api.jenzy.com/v1/collections/simulate"
payload = {
"amount_mwk": 50000,
"rail": "momo"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({amount_mwk: 50000, rail: 'momo'})
};
fetch('https://api.jenzy.com/v1/collections/simulate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.jenzy.com/v1/collections/simulate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount_mwk' => 50000,
'rail' => 'momo'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.jenzy.com/v1/collections/simulate"
payload := strings.NewReader("{\n \"amount_mwk\": 50000,\n \"rail\": \"momo\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.jenzy.com/v1/collections/simulate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"amount_mwk\": 50000,\n \"rail\": \"momo\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.jenzy.com/v1/collections/simulate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount_mwk\": 50000,\n \"rail\": \"momo\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "settled",
"amount_mwk": "1000.00",
"fee": "15.00",
"amount_credited": "985.00",
"rail": "bank",
"source_customer_name": "PETER BANDA",
"source_account_number": "265991234567",
"source_institution": "Airtel Money",
"rtp_reference_number": "<string>",
"created_at": "2023-11-07T05:31:56Z"
}{
"status": "pending"
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}{
"error": {
"code": "validation_error",
"message": "<string>"
}
}Authorizations
Your Jenzy API key: Authorization: Bearer jz_live_…
Body
What the pretend sender pays IN — the gross, before fees. You are credited less: the fee comes out of it, exactly as it does on a real pay-in. Either a JSON integer of whole kwacha or an exact decimal string with up to 2 decimal places ("1000.55"). Must be positive and no greater than 5000000000 MWK.
50000
Where the pretend sender pays from. Decides the rail and source_institution on the resulting collection; it does not change the price, which is the same on both. Defaults to momo.
momo, bank "momo"
Response
The collection your simulated pay-in became — already credited.
Collection id — your handle for polling and support.
settled means the money credited your balance. reversed means the provider then clawed the pay-in back — treat the collection.reversed event as authoritative; your balance catches up.
settled, reversed "settled"
The gross amount the sender sent — exact decimal string at 2 decimal places. Parse with a decimal library, never a float.
"1000.00"
The transaction fee, as one combined figure — an exact decimal string. Always equal to amount_mwk minus amount_credited. Use POST /fees/quote to know it in advance.
"15.00"
What your balance moved by: amount_mwk minus fee, as an exact decimal string.
"985.00"
How the money arrived: momo from a mobile wallet, bank from a bank account. Null when the rail could not be determined.
bank, momo, null Who sent the money, as reported by the provider.
"PETER BANDA"
The account or wallet the money came from, verbatim — your handle for matching the pay-in to your own customer.
"265991234567"
The institution the money came from, as reported by the provider.
"Airtel Money"
The reference you supplied when requesting this payment, echoed back for matching. Null when the payer sent the money directly.