API Documentation
Quick start
- Register and verify your email.
- Create an API key in Developer → API Keys (secret shown once).
- Optionally register a webhook endpoint.
- Create a deposit and redirect your customer to
checkout_url. - Receive the webhook, verify the signature and fulfill your order.
All API requests go to /api/v1/… on this domain. Use Idempotency-Key on all financial POSTs.
Authentication (HMAC-SHA256)
Send these headers on every request:
X-GROWZY-KEY: your key id (api_...)
X-GROWZY-TIMESTAMP: unix seconds
X-GROWZY-NONCE: random string (unique per request)
X-GROWZY-SIGNATURE: HMAC-SHA256 hex of the canonical string
The canonical signing string is (each field on its own line):
API_KEY
TIMESTAMP
NONCE
HTTP_METHOD
REQUEST_PATH
SHA256_HEX(raw_request_body)
Requests with timestamps outside the tolerance window (default 300s) or with reused nonces are rejected. Signatures are compared in constant time.
API_KEY="YOUR_API_KEY"
API_SECRET="YOUR_API_SECRET"
TS=$(date +%s)
NONCE=$(head -c16 /dev/urandom | xxd -p)
BODY='{"amount":"500.00"}'
BODY_HASH=$(printf '%s' "$BODY" | sha256sum | cut -d' ' -f1)
SIG=$(printf 'API_KEY
%s
%s
%s
%s
%s' "$TS" "$NONCE" "POST" "/api/v1/deposits" "$BODY_HASH" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')
curl -X POST https://wallet.growzylabs.in/api/v1/deposits \
-H "Content-Type: application/json" \
-H "X-GROWZY-KEY: $API_KEY" \
-H "X-GROWZY-TIMESTAMP: $TS" \
-H "X-GROWZY-NONCE: $NONCE" \
-H "X-GROWZY-SIGNATURE: $SIG" \
-H "Idempotency-Key: order-1234" \
-d "$BODY"<?php
$apiKey = 'YOUR_API_KEY';
$secret = 'YOUR_API_SECRET';
$ts = time();
$nonce = bin2hex(random_bytes(16));
$body = json_encode(['amount' => '500.00']);
$method = 'POST';
$path = '/api/v1/deposits';
$canonical = $apiKey."\n".$ts."\n".$nonce."\n".$method."\n".$path."\n".hash('sha256', $body);
$sig = hash_hmac('sha256', $canonical, $secret);
$ch = curl_init('https://wallet.growzylabs.in'.$path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-GROWZY-KEY: '.$apiKey,
'X-GROWZY-TIMESTAMP: '.$ts,
'X-GROWZY-NONCE: '.$nonce,
'X-GROWZY-SIGNATURE: '.$sig,
'Idempotency-Key: order-1234',
],
CURLOPT_RETURNTRANSFER => true,
]);
print_r(json_decode(curl_exec($ch), true));import hashlib, hmac, json, os, time, requests
api_key = 'YOUR_API_KEY'
secret = 'YOUR_API_SECRET'
ts = str(int(time.time()))
nonce = os.urandom(16).hex()
body = json.dumps({'amount': '500.00'})
path = '/api/v1/deposits'
canonical = '\n'.join([api_key, ts, nonce, 'POST', path, hashlib.sha256(body.encode()).hexdigest()])
sig = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()
r = requests.post('https://wallet.growzylabs.in' + path, data=body, headers={
'Content-Type': 'application/json',
'X-GROWZY-KEY': api_key,
'X-GROWZY-TIMESTAMP': ts,
'X-GROWZY-NONCE': nonce,
'X-GROWZY-SIGNATURE': sig,
'Idempotency-Key': 'order-1234',
})
print(r.json())const crypto = require('crypto');
const apiKey = 'YOUR_API_KEY';
const secret = 'YOUR_API_SECRET';
const ts = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(16).toString('hex');
const body = JSON.stringify({ amount: '500.00' });
const path = '/api/v1/deposits';
const canonical = [apiKey, ts, nonce, 'POST', path,
crypto.createHash('sha256').update(body).digest('hex')].join('\n');
const sig = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
fetch('https://wallet.growzylabs.in' + path, {
method: 'POST', body,
headers: {
'Content-Type': 'application/json',
'X-GROWZY-KEY': apiKey, 'X-GROWZY-TIMESTAMP': ts,
'X-GROWZY-NONCE': nonce, 'X-GROWZY-SIGNATURE': sig,
'Idempotency-Key': 'order-1234',
},
}).then(r => r.json()).then(console.log);Deposits
POST /api/v1/deposits { "amount": "500.00" }
GET /api/v1/deposits ?page=1&limit=20
GET /api/v1/deposits/{id}
POST /api/v1/deposits/{id}/utr { "utr": "123456789012" }
Scopes: deposits:create, deposits:read. Test-environment keys return a sandbox response and never touch real balances.
Wallet
GET /api/v1/wallet
GET /api/v1/wallet/balance
Scope: wallet:read.
Withdrawals
POST /api/v1/withdrawals { "amount": "500.00", "destination": "name@bank" }
GET /api/v1/withdrawals ?page=1
GET /api/v1/withdrawals/{id}
Scopes: withdrawals:create, withdrawals:read. Fees are computed server-side from platform settings.
Transactions
GET /api/v1/transactions ?type=deposit&status=completed
GET /api/v1/transactions/{id}
Scope: transactions:read.
Webhooks
POST /api/v1/webhooks { "url": "https://…", "events": ["deposit.approved"] }
GET /api/v1/webhooks
DELETE /api/v1/webhooks/{id}
Delivered events carry headers X-Growzy-Event, X-Growzy-Event-Id and X-Growzy-Signature: t=<ts>,v1=<hmac>. The HMAC is computed over <timestamp>.<payload> with your endpoint secret. Verify the signature and the timestamp before processing. Failed deliveries retry up to 5 times with exponential backoff. Every event has a unique event_id for safe de-duplication.
<?php
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_GROWZY_SIGNATURE'] ?? '';
[$t, $v1] = [null, null];
foreach (explode(',', $header) as $part) {
[$k, $v] = array_pad(explode('=', trim($part), 2), 2, null);
if ($k === 't') $t = $v; if ($k === 'v1') $v1 = $v;
}
$expected = hash_hmac('sha256', $t.'.'.$payload, 'YOUR_WEBHOOK_SECRET');
if (!hash_equals($expected, $v1 ?? '') || abs(time() - (int)$t) > 300) {
http_response_code(400); exit;
}
$event = json_decode($payload, true);
// de-duplicate on $event['event_id'], then fulfill orderIdempotency
Send Idempotency-Key: <unique-key> on every financial POST. If the same key is submitted again, the original response is returned (with header Idempotent-Replay: true) and no duplicate transaction is created.
Errors & status codes
200 OK success
201 Created resource created
400 INVALID_REQUEST malformed request
401 UNAUTHORIZED / INVALID_SIGNATURE
403 FORBIDDEN missing scope or permission
404 NOT_FOUND
422 VALIDATION_ERROR / INSUFFICIENT_BALANCE
429 RATE_LIMITED
500 INTERNAL_ERROR
Error responses use {"status":"error","error":{"code":…,"message":…},"meta":{"request_id":…}}. Never expose your secrets in support tickets — share the request_id instead.
Telegram integration
Connect a bot under Telegram in your dashboard. Customers send /pay 500 to your bot and receive a Growzy checkout link. Your bot can also call the API above to create sessions programmatically — see docs/TELEGRAM_EXAMPLES.md in the deployment package for PHP, Python and Node examples using YOUR_BOT_TOKEN.