Overview
Once you’ve validated all inputs, you’re ready to send the top-up request to the OneClickDz API. This guide covers submitting requests, handling responses, and managing your reference system.API Reference
View complete API documentation for POST /v3/mobile/send endpoint
Basic Top-Up Request
Simple Example
curl -X POST https://api.oneclickdz.com/v3/mobile/send \
-H "Content-Type: application/json" \
-H "X-Access-Token: YOUR_API_KEY" \
-d '{
"plan_code": "PREPAID_DJEZZY",
"MSSIDN": "0778037340",
"amount": 500,
"ref": "order-1730462400-user123"
}'
const fetch = require('node-fetch');
async function sendTopUp({ planCode, phone, amount, ref }) {
const response = await fetch('https://api.oneclickdz.com/v3/mobile/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Access-Token': process.env.ONECLICKDZ_API_KEY
},
body: JSON.stringify({
plan_code: planCode,
MSSIDN: phone,
amount: amount,
ref: ref
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Request failed');
}
const data = await response.json();
if (!data.success) {
throw new Error(data.error?.message || 'Top-up failed');
}
return {
topupId: data.data.topupId,
topupRef: data.data.topupRef,
newBalance: data.data.newBalance
};
}
// Usage
try {
const result = await sendTopUp({
planCode: 'PREPAID_DJEZZY',
phone: '0778037340',
amount: 500,
ref: `order-${Date.now()}-user123`
});
console.log('✅ Top-up sent:', result.topupId);
console.log('Reference:', result.topupRef);
console.log('New balance:', result.newBalance);
} catch (error) {
console.error('❌ Failed:', error.message);
}
import requests
import json
import os
import time
def send_topup(plan_code, phone, amount, ref):
"""Send a mobile top-up request"""
response = requests.post(
'https://api.oneclickdz.com/v3/mobile/send',
headers={
'Content-Type': 'application/json',
'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')
},
json={
'plan_code': plan_code,
'MSSIDN': phone,
'amount': amount,
'ref': ref
}
)
response.raise_for_status()
data = response.json()
if not data['success']:
raise Exception(data['error']['message'])
return {
'topup_id': data['data']['topupId'],
'topup_ref': data['data']['topupRef'],
'new_balance': data['data'].get('newBalance')
}
# Usage
try:
result = send_topup(
plan_code='PREPAID_DJEZZY',
phone='0778037340',
amount=500,
ref=f'order-{int(time.time())}-user123'
)
print(f"✅ Top-up sent: {result['topup_id']}")
print(f"Reference: {result['topup_ref']}")
print(f"New balance: {result.get('new_balance')}")
except Exception as error:
print(f"❌ Failed: {error}")
<?php
function sendTopUp($planCode, $phone, $amount, $ref) {
$ch = curl_init('https://api.oneclickdz.com/v3/mobile/send');
$data = [
'plan_code' => $planCode,
'MSSIDN' => $phone,
'amount' => $amount,
'ref' => $ref
];
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
],
CURLOPT_POSTFIELDS => json_encode($data)
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("HTTP error! status: $httpCode");
}
$result = json_decode($response, true);
if (!$result['success']) {
throw new Exception($result['error']['message'] ?? 'Top-up failed');
}
return [
'topup_id' => $result['data']['topupId'],
'topup_ref' => $result['data']['topupRef'],
'new_balance' => $result['data']['newBalance'] ?? null
];
}
// Usage
try {
$result = sendTopUp(
'PREPAID_DJEZZY',
'0778037340',
500,
'order-' . time() . '-user123'
);
echo "✅ Top-up sent: {$result['topup_id']}\n";
echo "Reference: {$result['topup_ref']}\n";
echo "New balance: {$result['new_balance']}\n";
} catch (Exception $error) {
echo "❌ Failed: {$error->getMessage()}\n";
}
Success Response
{
"success": true,
"data": {
"topupId": "6901616fe9e88196b4eb64b0",
"topupRef": "order-1730462400-user123"
},
"meta": {
"timestamp": "2025-11-01T11:00:00.454Z"
},
"requestId": "req_1730462400_abc123"
}
Generating Unique References
Always provide unique references to prevent duplicates and enable tracking:function generateOrderRef(userId) {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 8);
return `${userId}-${timestamp}-${random}`;
}
// Examples:
// user123-1730462400-x7k2p9
// user456-1730462401-m5n8q3
import time
import random
import string
def generate_order_ref(user_id):
timestamp = int(time.time() * 1000)
random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
return f"{user_id}-{timestamp}-{random_str}"
# Examples:
# user123-1730462400-x7k2p9
# user456-1730462401-m5n8q3
<?php
function generateOrderRef($userId) {
$timestamp = round(microtime(true) * 1000);
$random = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 6);
return "{$userId}-{$timestamp}-{$random}";
}
// Examples:
// user123-1730462400-x7k2p9
// user456-1730462401-m5n8q3
Checking Balance Before Sending
Always verify sufficient balance to prevent failed requests:Balance API Reference
Learn more about GET /v3/account/balance endpoint
async function checkBalance() {
const response = await fetch('https://api.oneclickdz.com/v3/account/balance', {
headers: {
'X-Access-Token': process.env.ONECLICKDZ_API_KEY
}
});
const data = await response.json();
if (!data.success) {
throw new Error('Failed to check balance');
}
return data.data.balance;
}
async function sendTopUpWithBalanceCheck({ planCode, phone, amount, ref, requiredBalance }) {
// Check balance first
const balance = await checkBalance();
if (balance < requiredBalance) {
throw new Error(`Insufficient balance. Required: ${requiredBalance} DZD, Available: ${balance} DZD`);
}
// Proceed with top-up
return await sendTopUp({ planCode, phone, amount, ref });
}
def check_balance():
"""Check account balance"""
response = requests.get(
'https://api.oneclickdz.com/v3/account/balance',
headers={'X-Access-Token': os.getenv('ONECLICKDZ_API_KEY')}
)
response.raise_for_status()
data = response.json()
if not data['success']:
raise Exception('Failed to check balance')
return data['data']['balance']
def send_topup_with_balance_check(plan_code, phone, amount, ref, required_balance):
"""Send top-up after checking balance"""
# Check balance first
balance = check_balance()
if balance < required_balance:
raise Exception(f'Insufficient balance. Required: {required_balance} DZD, Available: {balance} DZD')
# Proceed with top-up
return send_topup(plan_code, phone, amount, ref)
<?php
function checkBalance() {
$ch = curl_init('https://api.oneclickdz.com/v3/account/balance');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-Access-Token: ' . getenv('ONECLICKDZ_API_KEY')
]
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (!$data['success']) {
throw new Exception('Failed to check balance');
}
return $data['data']['balance'];
}
function sendTopUpWithBalanceCheck($planCode, $phone, $amount, $ref, $requiredBalance) {
// Check balance first
$balance = checkBalance();
if ($balance < $requiredBalance) {
throw new Exception("Insufficient balance. Required: {$requiredBalance} DZD, Available: {$balance} DZD");
}
// Proceed with top-up
return sendTopUp($planCode, $phone, $amount, $ref);
}
Error Handling
Handle common API errors:See the Error Handling Reference for complete error codes and handling strategies.
async function sendTopUpSafe(data) {
try {
return await sendTopUp(data);
} catch (error) {
// Parse error response
if (error.response) {
const errorData = error.response.data;
switch (errorData.error?.code) {
case 'ERR_VALIDATION':
console.error('Validation error:', errorData.error.message);
console.error('Field:', errorData.error.details?.field);
break;
case 'INSUFFICIENT_BALANCE':
console.error('Insufficient balance');
console.error('Required:', errorData.error.details?.required);
console.error('Available:', errorData.error.details?.available);
break;
case 'DUPLICATED_REF':
console.error('Duplicate reference');
console.error('Existing topup:', errorData.error.details?.existingTopupId);
// Check status of existing topup
break;
case 'INTERNAL_ERROR':
console.error('Server error, contact support');
console.error('Request ID:', errorData.requestId);
break;
default:
console.error('Unknown error:', errorData.error?.message);
}
}
throw error;
}
}
def send_topup_safe(data):
"""Send top-up with error handling"""
try:
return send_topup(**data)
except requests.HTTPError as error:
response_data = error.response.json()
error_code = response_data['error']['code']
if error_code == 'ERR_VALIDATION':
print('Validation error:', response_data['error']['message'])
print('Field:', response_data['error'].get('details', {}).get('field'))
elif error_code == 'INSUFFICIENT_BALANCE':
print('Insufficient balance')
print('Required:', response_data['error']['details']['required'])
print('Available:', response_data['error']['details']['available'])
elif error_code == 'DUPLICATED_REF':
print('Duplicate reference')
print('Existing topup:', response_data['error']['details']['existingTopupId'])
elif error_code == 'INTERNAL_ERROR':
print('Server error, contact support')
print('Request ID:', response_data['requestId'])
else:
print('Unknown error:', response_data['error']['message'])
raise
<?php
function sendTopUpSafe($data) {
try {
return sendTopUp(
$data['plan_code'],
$data['phone'],
$data['amount'],
$data['ref']
);
} catch (Exception $error) {
// Try to parse error response
$message = $error->getMessage();
if (strpos($message, 'Validation error') !== false) {
echo "Validation error\n";
} elseif (strpos($message, 'Insufficient balance') !== false) {
echo "Insufficient balance\n";
} elseif (strpos($message, 'Duplicate reference') !== false) {
echo "Duplicate reference - check existing order\n";
} else {
echo "Error: {$message}\n";
}
throw $error;
}
}
Database Integration
Store orders before and after API calls:async function createTopUpOrder({ userId, planCode, phone, amount }) {
// Start database transaction
const transaction = await db.transaction();
try {
// 1. Generate reference
const ref = generateOrderRef(userId);
// 2. Get plan details
const plan = await db.mobilePlans.findOne({
where: { code: planCode }
});
const cost = amount * plan.wholesaleCost;
// 3. Check user balance
const user = await db.users.findOne({
where: { id: userId },
lock: true, // Lock row for update
transaction
});
if (user.balance < cost) {
await transaction.rollback();
throw new Error('Insufficient user balance');
}
// 4. Create order record
const order = await db.orders.create({
userId,
ref,
planCode,
phone,
amount,
cost,
status: 'PENDING',
createdAt: new Date()
}, { transaction });
// 5. Deduct user balance
await db.users.update({
balance: user.balance - cost
}, {
where: { id: userId },
transaction
});
// 6. Send to API
const result = await sendTopUp({
planCode,
phone,
amount,
ref
});
// 7. Update order with API response
await db.orders.update({
apiTopupId: result.topupId,
apiTopupRef: result.topupRef,
status: 'PROCESSING'
}, {
where: { id: order.id },
transaction
});
// Commit transaction
await transaction.commit();
return {
orderId: order.id,
topupId: result.topupId,
ref: ref
};
} catch (error) {
// Rollback on any error
await transaction.rollback();
throw error;
}
}
def create_topup_order(user_id, plan_code, phone, amount, db):
"""Create top-up order with database transaction"""
with db.transaction() as transaction:
try:
# 1. Generate reference
ref = generate_order_ref(user_id)
# 2. Get plan details
plan = db.mobile_plans.find_one({'code': plan_code})
cost = amount * plan['wholesale_cost']
# 3. Check user balance
user = db.users.find_one_and_lock({'_id': user_id})
if user['balance'] < cost:
transaction.rollback()
raise Exception('Insufficient user balance')
# 4. Create order record
order = db.orders.insert_one({
'user_id': user_id,
'ref': ref,
'plan_code': plan_code,
'phone': phone,
'amount': amount,
'cost': cost,
'status': 'PENDING',
'created_at': datetime.now()
})
# 5. Deduct user balance
db.users.update_one(
{'_id': user_id},
{'$inc': {'balance': -cost}}
)
# 6. Send to API
result = send_topup(plan_code, phone, amount, ref)
# 7. Update order with API response
db.orders.update_one(
{'_id': order.inserted_id},
{
'$set': {
'api_topup_id': result['topup_id'],
'api_topup_ref': result['topup_ref'],
'status': 'PROCESSING'
}
}
)
transaction.commit()
return {
'order_id': str(order.inserted_id),
'topup_id': result['topup_id'],
'ref': ref
}
except Exception as error:
transaction.rollback()
raise
<?php
function createTopUpOrder($userId, $planCode, $phone, $amount, $db) {
$db->beginTransaction();
try {
// 1. Generate reference
$ref = generateOrderRef($userId);
// 2. Get plan details
$plan = $db->query(
"SELECT * FROM mobile_plans WHERE code = ? FOR UPDATE",
[$planCode]
)->fetch(PDO::FETCH_ASSOC);
$cost = $amount * $plan['wholesale_cost'];
// 3. Check user balance
$user = $db->query(
"SELECT * FROM users WHERE id = ? FOR UPDATE",
[$userId]
)->fetch(PDO::FETCH_ASSOC);
if ($user['balance'] < $cost) {
$db->rollBack();
throw new Exception('Insufficient user balance');
}
// 4. Create order record
$db->query(
"INSERT INTO orders (user_id, ref, plan_code, phone, amount, cost, status, created_at)
VALUES (?, ?, ?, ?, ?, ?, 'PENDING', NOW())",
[$userId, $ref, $planCode, $phone, $amount, $cost]
);
$orderId = $db->lastInsertId();
// 5. Deduct user balance
$db->query(
"UPDATE users SET balance = balance - ? WHERE id = ?",
[$cost, $userId]
);
// 6. Send to API
$result = sendTopUp($planCode, $phone, $amount, $ref);
// 7. Update order with API response
$db->query(
"UPDATE orders
SET api_topup_id = ?, api_topup_ref = ?, status = 'PROCESSING'
WHERE id = ?",
[$result['topup_id'], $result['topup_ref'], $orderId]
);
$db->commit();
return [
'order_id' => $orderId,
'topup_id' => $result['topup_id'],
'ref' => $ref
];
} catch (Exception $error) {
$db->rollBack();
throw $error;
}
}
Async Processing
For better performance, process top-ups asynchronously:const Queue = require('bull');
const topupQueue = new Queue('topup-processing');
// Add job to queue
async function queueTopUp(orderData) {
const order = await db.orders.create({
...orderData,
status: 'QUEUED'
});
await topupQueue.add({
orderId: order.id
});
return order;
}
// Process queue
topupQueue.process(async (job) => {
const { orderId } = job.data;
const order = await db.orders.findOne({ where: { id: orderId } });
try {
// Send top-up
const result = await sendTopUp({
planCode: order.planCode,
phone: order.phone,
amount: order.amount,
ref: order.ref
});
// Update order
await db.orders.update({
apiTopupId: result.topupId,
status: 'PROCESSING'
}, {
where: { id: orderId }
});
// Start status polling (another queue)
await pollQueue.add({ topupId: result.topupId, orderId });
} catch (error) {
// Handle error
await db.orders.update({
status: 'FAILED',
errorMessage: error.message
}, {
where: { id: orderId }
});
// Refund user
await refundUser(order.userId, order.cost);
}
});
from celery import Celery
app = Celery('topup', broker='redis://localhost:6379/0')
@app.task
def process_topup(order_id):
"""Process top-up asynchronously"""
order = db.orders.find_one({'_id': order_id})
try:
# Send top-up
result = send_topup(
order['plan_code'],
order['phone'],
order['amount'],
order['ref']
)
# Update order
db.orders.update_one(
{'_id': order_id},
{
'$set': {
'api_topup_id': result['topup_id'],
'status': 'PROCESSING'
}
}
)
# Start status polling
poll_topup_status.delay(result['topup_id'], order_id)
except Exception as error:
# Handle error
db.orders.update_one(
{'_id': order_id},
{
'$set': {
'status': 'FAILED',
'error_message': str(error)
}
}
)
# Refund user
refund_user(order['user_id'], order['cost'])
# Queue top-up
def queue_topup(order_data):
order = db.orders.insert_one({
**order_data,
'status': 'QUEUED'
})
process_topup.delay(str(order.inserted_id))
return order
Retry Logic
Implement retry for network errors:async function sendTopUpWithRetry(data, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await sendTopUp(data);
} catch (error) {
lastError = error;
// Don't retry on validation or duplicate errors
if (error.code === 'ERR_VALIDATION' || error.code === 'DUPLICATED_REF') {
throw error;
}
// Wait before retry (exponential backoff)
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
import time
def send_topup_with_retry(data, max_retries=3):
"""Send top-up with retry logic"""
last_error = None
for attempt in range(1, max_retries + 1):
try:
return send_topup(**data)
except Exception as error:
last_error = error
# Don't retry on validation or duplicate errors
error_msg = str(error)
if 'Validation' in error_msg or 'Duplicate' in error_msg:
raise
# Wait before retry (exponential backoff)
if attempt < max_retries:
delay = 2 ** attempt # 2s, 4s, 8s
print(f'Retry {attempt}/{max_retries} after {delay}s...')
time.sleep(delay)
raise last_error
<?php
function sendTopUpWithRetry($data, $maxRetries = 3) {
$lastError = null;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
try {
return sendTopUp(
$data['plan_code'],
$data['phone'],
$data['amount'],
$data['ref']
);
} catch (Exception $error) {
$lastError = $error;
// Don't retry on validation or duplicate errors
$errorMsg = $error->getMessage();
if (strpos($errorMsg, 'Validation') !== false ||
strpos($errorMsg, 'Duplicate') !== false) {
throw $error;
}
// Wait before retry (exponential backoff)
if ($attempt < $maxRetries) {
$delay = pow(2, $attempt); // 2s, 4s, 8s
echo "Retry {$attempt}/{$maxRetries} after {$delay}s...\n";
sleep($delay);
}
}
}
throw $lastError;
}
Best Practices
Always Use Unique References
Always Use Unique References
Generate unique
ref for each request to prevent duplicates and enable tracking.Check Balance First
Check Balance First
Verify sufficient balance before sending to avoid failed requests.
Use Database Transactions
Use Database Transactions
Wrap order creation and balance deduction in transactions for data consistency.
Process Asynchronously
Process Asynchronously
Use queues for better performance and user experience.
Implement Retry Logic
Implement Retry Logic
Retry network errors with exponential backoff, but not validation errors.
Log Everything
Log Everything
Log all requests and responses for debugging and auditing.
Next Steps
Step 4: Status Polling
Learn how to poll and track top-up status
Check Status by Ref API
API docs for GET /v3/mobile/check-ref/:ref
Check Status by ID API
API docs for GET /v3/mobile/check-id/:id
Error Handling Reference
Complete error handling and recovery guide
Webhooks Setup
Set up real-time status notifications
Polling Strategies
Optimize your polling implementation
Transaction History API
View your transaction history
Balance API
Check account balance endpoint

