Live API Reference

HashBack & HashPay APIs

Complete REST API reference for MSISDN decoding, M-Pesa STK Push payments, Wallet B2C transfers, and transaction lookups. Every endpoint documented with request examples in cURL, JavaScript, PHP, and Python.

API Key Auth <50ms decode Kenya (Safaricom & Airtel) M-Pesa STK Push

HashBack Decode API

Decode hashed MSISDNs to real phone numbers. Safaricom & Airtel supported.

Payment Button

Drop-in “Pay with M-PESA” popup — one line of JavaScript, public account ID only.

Wallet B2C

Check balances, top up via STK Push, and send money to customers.

PULL API

Look up any transaction by ID for reconciliation and reporting.

HashPeer P2P

Sync M-Pesa SMS transactions, confirm payments, and query by code or details.

Authentication

All API requests require authentication via your API key. Pass it either as a request body field or as an HTTP header depending on the endpoint.

Generate your API key from the Settings tab in your HashBack dashboard after registration.

Header method (HashBack Decode)

HTTP Header
API_KEY: YOUR_API_KEY_HERE

Body method (HashPay endpoints)

JSON Body
{
  "api_key": "YOUR_API_KEY_HERE",
  "account_id": "YOUR_ACCOUNT_ID"
}

Base URL

All endpoints are served over HTTPS. Use the correct versioned path for each product.

Base URLs
# HashBack Decode
https://api.hashback.co.ke/

# HashPay STK + PULL
https://api.hashback.co.ke/

# Wallet B2C V2 (current)
https://api.hashback.co.ke/V2/

Rate Limiting

Requests are throttled per API key to protect service stability.

Endpoint groupLimitWindow
All endpoints100 requestsPer minute
HashBack Decode10 requestsPer 5 seconds
When rate limited you receive HTTP 429. Use exponential backoff before retrying.
429 Response
{
  "error": {
    "code":    429,
    "message": "Too many requests. Please try again later."
  }
}

HashBack Decode API

Decode hashed phone numbers back to their real MSISDN format. Supports both Safaricom and Airtel Kenya. Typical response time is under 50 ms.

Decode MSISDN Hash
POST https://api.hashback.co.ke/decode

Request parameters

ParameterTypeRequiredDescription
hashStringRequiredThe MSISDN hash string to decode
API_KEYStringRequiredYour API key — passed as an HTTP header

Code examples

bash
curl -X POST https://api.hashback.co.ke/decode \
  -d 'hash=4f87c55d393937f18fbf3003512195aa8e62be340946ab547c2eada26cc43c1e' \
  -H 'API_KEY: YOUR_API_KEY_HERE'
javascript
const res = await fetch('https://api.hashback.co.ke/decode', {
  method: 'POST',
  headers: { 'API_KEY': 'YOUR_API_KEY_HERE' },
  body: new URLSearchParams({
    hash: '4f87c55d393937f18fbf3003512195aa8e62be340946ab547c2eada26cc43c1e'
  })
});

const { MSISDN } = await res.json();
console.log(MSISDN); // "254712345678"
php
<?php
$ch = curl_init('https://api.hashback.co.ke/decode');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
  http_build_query(['hash' => '4f87c55d...'])
);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['API_KEY: YOUR_API_KEY_HERE']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$r = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $r['MSISDN']; // 254712345678
?>
python
import requests

r = requests.post(
    'https://api.hashback.co.ke/decode',
    data={'hash': '4f87c55d...'},
    headers={'API_KEY': 'YOUR_API_KEY_HERE'}
)
print(r.json()['MSISDN'])  # 254712345678

Response

200 OK
{
  "ResultCode": "0",
  "MSISDN":     "254712345678"
}

Payment Button

A drop-in “Pay with M-PESA” popup. Embed one script and your public account_id — no secret key ever touches the browser. The button opens a hosted M-PESA modal, fires the STK push server-side, polls for the result, and calls your success / cancel / error handlers.

How it works

Three steps — the merchant only ever exposes their public account ID.

StepWhat happens
1. EmbedLoad hashpay.js and call HashPay.setup({...}) with your public account_id and the amount.
2. PayThe popup collects the payer's phone and fires the M-PESA STK push server-side, returning a checkout_id.
3. ConfirmThe popup polls status until the M-PESA callback records the result, then shows success / failed / cancelled and calls your handler.
The account_id is a public key — safe to expose. It can only start a payment into your own till/paybill. Your secret api_key is never used client-side.
Live Demo

Tap the button below — you get STK prompt to enter pin and complete checkout.

This live button loads hashpay.js from the CDN and calls HashPay.pay({...}) — exactly the snippet below.

Integration

Drop this into any page — just add the CDN <script> and a button.

html
<script src="https://pay.hashback.co.ke/hashpay.js"></script>

<button id="pay">Pay KES 100</button>
<script>
  var handler = HashPay.setup({
    account:   'HP945692',      // your public account id (required)
    amount:    100,             // KES (required)
    reference: 'order_12345',   // optional — auto-generated if omitted
    onSuccess: function (txn) { alert('Paid! Receipt: ' + txn.receipt); },
    onCancel:  function () { console.log('closed'); },
    onError:   function (e) { console.error(e); }
  });
  document.getElementById('pay').addEventListener('click', handler.openIframe);
</script>
html
<script src="https://pay.hashback.co.ke/hashpay.js"></script>

<button class="hashpay-button"
        data-account="HP945692"
        data-amount="100"
        data-reference="order_12345">Pay KES 100</button>

No JavaScript needed. Auto-bound buttons emit DOM events on themselves: hashpay:success, hashpay:cancel, hashpay:error (payload on event.detail).

onSuccess payload

JSON
{
  "reference":  "order_12345",
  "receipt":    "SGH3XYZ123",     // M-PESA receipt number
  "amount":     100,
  "checkoutid": "ws_CO_...",
  "status":     "success"
}
Always validate the amount before fulfilling. Because this is a client-only integration the amount lives in the page and a payer could edit it in the browser dev tools. In your onSuccess handler, compare txn.amount against the real price of the package or order the customer selected — and only release the goods, service, or subscription if they match. Never trust the amount that comes back from the button on its own; check it against what the order should actually cost.
validate the amount against your order
// The real price of the package/order the customer picked.
const expectedAmount = ORDER.package.price;   // e.g. 1000 — from YOUR data, not the page

HashPay.setup({
  account: 'HP945692',
  amount:  expectedAmount,
  reference: ORDER.id,
  onSuccess: function (txn) {
    // Guard: confirm the paid amount matches what the order should cost.
    if (Number(txn.amount) !== Number(expectedAmount)) {
      // Amount was tampered with — do NOT fulfil. Flag / refund / contact support.
      console.error('Amount mismatch', txn.amount, 'expected', expectedAmount);
      return;
    }
    // Amounts match — safe to release the package / order.
    fulfilOrder(ORDER.id, txn.receipt);
  }
});
Webhook Callbacks

HashPay sends a POST request to your webhook URL when a payment transaction completes. Configure your URL in the Settings tab of your HashPay portal.

Set up your webhook URL before initiating transactions. The URL must be a public HTTPS endpoint that responds with a 2xx status.

Incoming request headers

HeaderValueNotes
Content-Typeapplication/jsonBody is always JSON
X-Hashpay-Signaturesha256=<hex-digest>Always verify this before processing
X-Forwarded-ForHashPay origin IPMay be set by proxy/load balancer
X-Forwarded-ProtohttpsConfirms HTTPS delivery

Webhook payload (success)

JSON — Incoming POST to your server
{
  "event":                 "payment.success",
  "ResponseCode":         0,
  "ResponseDescription":  "Success. Request accepted for processing",
  "MerchantRequestID":    "ws_CO_12052026084940",
  "CheckoutRequestID":    "ws_CO_12052026084940776662",
  "TransactionID":        "UEC496402X",
  "TransactionAmount":    1,
  "TransactionReceipt":   "UEC496402X",
  "TransactionDate":      20260512084950,
  "TransactionReference": "HPL1XBF0",
  "Msisdn":               254701234567,
  "AccountID":            "HP56"
}

Verifying the X-Hashpay-Signature

Every webhook request includes an X-Hashpay-Signature header containing an HMAC-SHA256 digest of the raw request body, signed with your webhook secret (found in the HashPay Settings portal). Always verify this signature before trusting the payload — reject any request that fails the check with a 401 response.

Compute the HMAC over the raw, unmodified request body bytes — not a re-serialised JSON string. Any whitespace difference will cause a mismatch.
php
<?php
// Retrieve raw body BEFORE reading $_POST or json_decode()
$rawBody       = file_get_contents('php://input');
$secret        = 'YOUR_WEBHOOK_SECRET';  // from HashPay Settings
$sigHeader     = $_SERVER['HTTP_X_HASHPAY_SIGNATURE'] ?? '';

// Header format: "sha256=<hex>"
$expected      = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);

if (!hash_equals($expected, $sigHeader)) {
    http_response_code(401);
    exit('Invalid signature');
}

// Signature valid — safe to process
$payload = json_decode($rawBody, true);

if ($payload['event'] === 'payment.success' && $payload['ResponseCode'] === 0) {
    $txid = $payload['TransactionID'];    // "UEC496402X"
    $ref  = $payload['TransactionReference']; // your order ref
    $msisdn = $payload['Msisdn'];           // 254701234567
    // fulfil the order ...
}

http_response_code(200);
?>
node.js
const crypto = require('crypto');

// Express example — use express.raw() to get the raw buffer
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const secret    = 'YOUR_WEBHOOK_SECRET';
  const sigHeader = req.headers['x-hashpay-signature'] ?? '';
  const expected  = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(req.body)          // raw Buffer
    .digest('hex');

  const valid = crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(sigHeader)
  );

  if (!valid) return res.status(401).send('Invalid signature');

  const payload = JSON.parse(req.body.toString());

  if (payload.event === 'payment.success' && payload.ResponseCode === 0) {
    const { TransactionID, TransactionReference, Msisdn } = payload;
    // fulfil the order ...
  }

  res.status(200).send('OK');
});
python
import hmac, hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b'YOUR_WEBHOOK_SECRET'

@app.route('/webhook', methods=['POST'])
def webhook():
    raw_body   = request.get_data()   # raw bytes, before any parsing
    sig_header = request.headers.get('X-Hashpay-Signature', '')

    expected = 'sha256=' + hmac.new(
        SECRET, raw_body, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, sig_header):
        abort(401)

    payload = request.get_json()

    if payload.get('event') == 'payment.success' and payload.get('ResponseCode') == 0:
        tx_id = payload['TransactionID']         # "UEC496402X"
        ref   = payload['TransactionReference']   # your order ref
        # fulfil the order ...

    return '', 200
Use constant-time comparison (hash_equals / hmac.compare_digest / timingSafeEqual) to prevent timing-attack leaks. Never use === or == for signature comparison.

Pay Button for Apps & Bots

Add a “Pay” button inside your Android app or Telegram bot. When the customer taps it, HashPay instantly creates a secure payment link and sends the customer straight to the payment page to pay with M-PESA — no typing, no copying links.

Prefer to start from working code? Grab the ready-made sample project — web button, Telegram bot, Android and webhook, all runnable: Hashback-Solutions/hashpaybuttonintegration.
How it works
StepWhat the customer sees
1. TapThe customer taps your Pay button in your app or Telegram bot.
2. Link createdA payment link is generated automatically for that exact amount — valid for 1 hour.
3. Redirected to payThe customer is taken to the secure HashPay payment page, enters their number and M-PESA PIN.
4. Back to youAfter paying they’re returned to your app or bot, and you’re notified the payment succeeded.
The amount is locked when the link is created, so the customer can’t change it. Generating these links is free — no service credit is used.
In a Telegram bot

Your bot shows a Pay button in the chat. The customer taps it and is taken to the HashPay payment page to pay with M-PESA. Once done, they’re brought back to the chat and the bot confirms the payment.

In your Android app

Place a Pay button anywhere in your app — on a cart, an order or a subscription screen. Tapping it opens the HashPay payment page; after paying, the customer lands right back in your app.

Wallet B2C API

Manage your HashPay wallet — check the balance, top up via STK Push, and send B2C withdrawals to customer phone numbers.

Check Wallet Balance
POST https://api.hashback.co.ke/walletbalance
ParameterTypeRequiredDescription
api_keyStringRequiredYour API key
account_idStringRequiredYour HashPay wallet ID
php
<?php
$ctx = stream_context_create(['http' => [
  'method'  => 'POST',
  'header'  => 'Content-type: application/json',
  'content' => json_encode([
    'api_key'    => 'KEY',
    'account_id' => 'WALLET_ID'
  ])
]]);
$r = json_decode(
  file_get_contents('https://api.hashback.co.ke/walletbalance', false, $ctx),
  true
);
echo "Balance: KES " . $r['balance'];
?>
javascript
const res = await fetch('https://api.hashback.co.ke/walletbalance', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ api_key: 'KEY', account_id: 'WALLET_ID' })
});
const { balance, status } = await res.json();
console.log(`KES ${balance} — ${status}`);
python
import requests

r = requests.post(
    'https://api.hashback.co.ke/walletbalance',
    json={'api_key': 'KEY', 'account_id': 'WALLET_ID'}
)
data = r.json()
print(f"KES {data['balance']} — {data['status']}")

Response

200 OK
{
  "success":  true,
  "walletId": "WALLET_ID",
  "balance":  47,
  "status":   "Active",
  "currency": "KES"
}
Top Up Wallet via STK Push
POST https://api.hashback.co.ke/v2/topup
ParameterTypeRequiredDescription
api_keyStringRequiredYour API key
walletidStringRequiredYour HashPay wallet ID
amountStringRequiredTop-up amount in KES
msisdnStringRequiredNominated phone number (must match portal)
The STK Push only succeeds when the customer pays using the nominated number registered in your HashPay portal.
javascript
const res = await fetch('https://api.hashback.co.ke/v2/topup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    api_key:  'KEY',
    walletid: 'WALLET_ID',
    amount:   '100',
    msisdn:   '0712345678'
  })
});
Process Withdrawal (B2C)
V1 Obsolete: /processwithdrawal was removed after October 26, 2025. Use V2 below.
POST https://api.hashback.co.ke/V2/processwithdrawal
ParameterTypeRequiredDescription
api_keyStringRequiredYour API key
msisdnStringRequiredPhone number to receive the withdrawal
amountStringRequiredWithdrawal amount in KES
SecurityCredentialStringRequiredSecurity credential from your HashPay portal
python
import requests

r = requests.post(
    'https://api.hashback.co.ke/V2/processwithdrawal',
    json={
        "api_key":            "KEY",
        "msisdn":             "07123456789",
        "amount":             20,
        "SecurityCredential": "CRED"
    }
)
print(r.json())
200 OK — Success
{
  "success": true,
  "message": "Withdrawal processed successfully",
  "details": {
    "amount":  50,
    "fee":     5,
    "total":   55,
    "balance": 92
  }
}
200 — Insufficient Funds
{
  "success": false,
  "message": "Insufficient funds. You need KES 18.00 more"
}

PULL API

Retrieve detailed information about any transaction by ID. Use this for reconciliation, auditing, and generating receipts.

Get Transaction Details
POST https://api.hashback.co.ke/v1/pullapi
ParameterTypeRequiredDescription
api_keyStringRequiredYour API key
account_idStringRequiredYour HashPay account ID
transaction_idStringRequiredThe transaction ID to retrieve
bash
curl -X POST https://api.hashback.co.ke/v1/pullapi \
  -H 'Content-Type: application/json' \
  -d '{
    "api_key":        "API_KEY",
    "account_id":     "ACCOUNT_ID",
    "transaction_id": "TRANS_ID"
  }'
200 — Found
{
  "success": true,
  "data": {
    "transactionId": "TRANS_ID",
    "amount":        499,
    "billreference": "BILL_REF",
    "AccName":       "ACC NAME"
  }
}
404 — Not Found
{
  "success": false,
  "message": "Transaction not found"
}

HashPeer P2P API

HashPeer P2P is a payment confirmation layer built on top of M-Pesa. Merchants integrate to confirm incoming payments and query transaction status in real time. The P2P base URL is separate from the HashBack API.

Base URL: https://p2p.hashback.co.ke — all P2P endpoints use this domain.
Merchant-facing endpoints (/api/v1/) use an X-Api-Key header and consume one credit per call. The Confirm Payment endpoint uses a JWT Bearer token obtained via the HashPeer app.
Merchant API v1 — API Key Auth — 1 credit per call
GET https://p2p.hashback.co.ke/api/v1/query?tx_code=QH7X8LKJ22

External merchant endpoint to query a transaction by code. Authenticated with an API key header, not a JWT. Consumes 1 credit and fires a payment.queried webhook. Rate-limited to 60 requests per 60 seconds per key.

Header / ParamTypeRequiredDescription
X-Api-Key (header)stringrequiredYour merchant API key from the HashPeer dashboard
tx_code (query string)stringrequiredM-Pesa transaction code to look up
Rate limit headers are returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

Code examples

cURL
curl -G https://p2p.hashback.co.ke/api/v1/query \
  -H "X-Api-Key: YOUR_API_KEY" \
  --data-urlencode "tx_code=QH7X8LKJ22"
JavaScript
const res = await fetch(
  'https://p2p.hashback.co.ke/api/v1/query?tx_code=QH7X8LKJ22',
  { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
);
const data = await res.json();
PHP
$ch = curl_init(
    'https://p2p.hashback.co.ke/api/v1/query?tx_code=' . urlencode('QH7X8LKJ22')
);
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER     => ['X-Api-Key: YOUR_API_KEY'],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);

Success response 200 OK

JSON
{
  "success": true,
  "message": "Transaction found.",
  "transaction": {
    "id": 1234,
    "tx_code": "QH7X8LKJ22",
    "type": "RECEIVED",
    "amount": 1500,
    "contact": "0704971999",
    "contact_name": "John Doe",
    "tx_timestamp": 1716800000000,
    "status": 0,
    "status_label": "pending",
    "confirmed_at": null,
    "added_at": "2026-05-27 10:30:00"
  }
}
POST https://p2p.hashback.co.ke/api/v1/confirm

External merchant endpoint to confirm a pending payment. Accepts the same Option A (by tx_code) and Option B (by amount + details) payloads as the app endpoint. Only RECEIVED-type transactions can be confirmed. Consumes 1 credit and fires a payment.confirmed webhook.

HeaderValue
X-Api-KeyYour merchant API key
Content-Typeapplication/json

Request body — same as app confirm

cURL — by tx_code
curl -X POST https://p2p.hashback.co.ke/api/v1/confirm \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tx_code": "QH7X8LKJ22"}'
cURL — by amount + details
curl -X POST https://p2p.hashback.co.ke/api/v1/confirm \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1500,
    "name": "John Doe",
    "phone": "0704971999"
  }'
JavaScript
const res = await fetch('https://p2p.hashback.co.ke/api/v1/confirm', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: 1500,
    name: 'John',        // partial name match works
    phone: '0704***999'  // masked phone works too
  })
});
const data = await res.json();

Success response 200 OK

JSON
{
  "success": true,
  "message": "Payment confirmed.",
  "transaction": {
    "id": 1234,
    "tx_code": "QH7X8LKJ22",
    "type": "RECEIVED",
    "amount": 1500,
    "contact": "0704971999",
    "contact_name": "John Doe",
    "tx_timestamp": 1716800000000,
    "status": 1,
    "confirmed_at": "2026-05-27 10:40:00",
    "added_at": "2026-05-27 10:30:00"
  }
}

P2P Webhooks

HashPeer fires a POST to each of your active webhook URLs after store (payment.received), app confirm (payment.confirmed), and API v1 query / confirm. Configure webhooks in the HashPeer dashboard.

Incoming request headers

HeaderDescription
Content-Typeapplication/json
X-HashPeer-SignatureHMAC-SHA256 of the raw JSON body signed with your webhook secret (only present if a secret is configured)

Webhook payload

JSON — payment.received / payment.confirmed / payment.queried
{
  "event": "payment.confirmed",
  "transaction": {
    "id": 1234,
    "tx_code": "QH7X8LKJ22",
    "type": "RECEIVED",
    "amount": 1500,
    "contact": "0704971999",
    "contact_name": "John Doe",
    "tx_timestamp": 1716800000000,
    "status": 1,
    "confirmed_at": "2026-05-27 10:40:00",
    "added_at": "2026-05-27 10:30:00"
  }
}

Verifying the X-HashPeer-Signature

PHP
$secret  = 'YOUR_WEBHOOK_SECRET';
$raw     = file_get_contents('php://input');
$sig     = $_SERVER['HTTP_X_HASHPEER_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $raw, $secret);

if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('Signature mismatch');
}

$payload = json_decode($raw, true);
$event   = $payload['event'];        // "payment.confirmed"
$tx      = $payload['transaction'];  // full transaction object
Node.js / Express
const crypto = require('crypto');

app.post('/webhook/hashpeer', express.raw({ type: 'application/json' }), (req, res) => {
  const secret   = process.env.HASHPEER_WEBHOOK_SECRET;
  const sig      = req.headers['x-hashpeer-signature'];
  const expected = crypto
    .createHmac('sha256', secret)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.status(401).send('Signature mismatch');
  }

  const { event, transaction } = JSON.parse(req.body);
  // handle event …
  res.status(200).end();
});
Python (Flask)
import hmac, hashlib
from flask import request, abort

SECRET = b'YOUR_WEBHOOK_SECRET'

@app.route('/webhook/hashpeer', methods=['POST'])
def hashpeer_webhook():
    sig      = request.headers.get('X-HashPeer-Signature', '')
    expected = hmac.new(SECRET, request.data, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        abort(401)
    payload = request.get_json()
    event   = payload['event']
    tx      = payload['transaction']
    return '', 200

Transaction object fields

FieldTypeDescription
idintegerDatabase row ID
tx_codestringM-Pesa transaction code, e.g. QH7X8LKJ22
typestringRECEIVED | SENT | WITHDRAW | PAYMENT | REVERSAL | OTHER
amountfloatAmount in KES
contactstringPhone number (may be masked, e.g. 0704***999)
contact_namestring|nullSender / recipient name from SMS
tx_timestampintegerUnix timestamp in milliseconds
statusinteger0 = pending, 1 = confirmed
status_labelstring"pending" | "confirmed" (query endpoints only)
confirmed_atstring|nullISO-8601 datetime when confirmed, otherwise null
added_atstringISO-8601 datetime when the record was created

Error Codes

All endpoints follow standard HTTP status codes. Every error response includes a message field with a human-readable description.

StatusNameDescriptionFix
200 OK Request completed successfully No action needed
400 Bad Request Missing or invalid parameters in the request body Validate all required fields
401 Unauthorized API key is missing, invalid, or expired Check your API key in Settings
429 Too Many Requests Rate limit exceeded for the API key Implement exponential backoff
500 Internal Server Error An unexpected error occurred on the server Retry once; contact support if it persists

Support

Get real-time help

Join the WhatsApp developer support group for API questions, integration help, and change notifications from the HashBack team.

Join WhatsApp Support

Email: hashbacksolutions@gmail.com