Transaction History
View and manage your billing transactions, including credit purchases, usage, and refunds.
Overview
The Transactions API provides access to your complete billing history. Track:
- Credit purchases
- Message usage (credits spent)
- Automatic refunds for failed messages
- Manual adjustments
Get Transactions
Retrieve transaction history with filtering and pagination.
Endpoint
GET /api/v1/billing/transactionsAuthentication
Requires JWT token (dashboard authentication).
Authorization: Bearer YOUR_JWT_TOKENQuery Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Filter by type: purchase, usage, refund, adjustment |
startDate | string | Filter from date (ISO 8601 format) |
endDate | string | Filter until date (ISO 8601 format) |
page | integer | Page number (default: 1) |
limit | integer | Items per page (default: 50, max: 100) |
Request
bash
# Get all transactions
curl -X GET "https://api.yebolink.com/api/v1/billing/transactions" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Filter by type
curl -X GET "https://api.yebolink.com/api/v1/billing/transactions?type=purchase" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Filter by date range
curl -X GET "https://api.yebolink.com/api/v1/billing/transactions?startDate=2025-10-01T00:00:00Z&endDate=2025-11-02T23:59:59Z" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"javascript
async function getTransactions(filters = {}) {
const params = new URLSearchParams(filters);
const response = await fetch(
`https://api.yebolink.com/api/v1/billing/transactions?${params}`,
{
headers: {
'Authorization': `Bearer ${jwtToken}`
}
}
);
return await response.json();
}
// Usage examples
// Get all transactions
const allTransactions = await getTransactions();
// Get only purchases
const purchases = await getTransactions({ type: 'purchase' });
// Get usage for last month
const lastMonth = await getTransactions({
type: 'usage',
startDate: '2025-10-01T00:00:00Z',
endDate: '2025-10-31T23:59:59Z'
});
// Paginated results
const page2 = await getTransactions({ page: 2, limit: 50 });python
import requests
from datetime import datetime, timedelta
def get_transactions(jwt_token: str, filters: dict = None):
params = filters or {}
response = requests.get(
'https://api.yebolink.com/api/v1/billing/transactions',
headers={'Authorization': f'Bearer {jwt_token}'},
params=params
)
return response.json()
# Usage examples
# Get all transactions
all_transactions = get_transactions(jwt_token)
# Get only purchases
purchases = get_transactions(jwt_token, {'type': 'purchase'})
# Get usage for last 30 days
end_date = datetime.now()
start_date = end_date - timedelta(days=30)
usage = get_transactions(jwt_token, {
'type': 'usage',
'startDate': start_date.isoformat() + 'Z',
'endDate': end_date.isoformat() + 'Z'
})
for txn in usage['data']['transactions']:
print(f"{txn['created_at']}: -{txn['amount']} credits - {txn['description']}")php
<?php
function getTransactions($jwtToken, $filters = []) {
$params = http_build_query($filters);
$url = "https://api.yebolink.com/api/v1/billing/transactions";
if ($params) {
$url .= "?$params";
}
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $jwtToken
]
]);
$response = curl_exec($curl);
curl_close($curl);
return json_decode($response, true);
}
// Usage examples
// Get all transactions
$allTransactions = getTransactions($jwtToken);
// Get only purchases
$purchases = getTransactions($jwtToken, ['type' => 'purchase']);
// Get usage for October 2025
$octTransactions = getTransactions($jwtToken, [
'type' => 'usage',
'startDate' => '2025-10-01T00:00:00Z',
'endDate' => '2025-10-31T23:59:59Z'
]);
foreach ($octTransactions['data']['transactions'] as $txn) {
echo $txn['created_at'] . ": " . $txn['amount'] . " credits - " . $txn['description'] . "\n";
}
?>ruby
require 'net/http'
require 'json'
require 'uri'
def get_transactions(jwt_token, filters = {})
uri = URI('https://api.yebolink.com/api/v1/billing/transactions')
uri.query = URI.encode_www_form(filters) unless filters.empty?
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{jwt_token}"
response = http.request(request)
JSON.parse(response.body)
end
# Usage examples
# Get all transactions
all_transactions = get_transactions(jwt_token)
# Get only purchases
purchases = get_transactions(jwt_token, { type: 'purchase' })
# Get usage for last month
usage = get_transactions(jwt_token, {
type: 'usage',
startDate: '2025-10-01T00:00:00Z',
endDate: '2025-10-31T23:59:59Z'
})
usage['data']['transactions'].each do |txn|
puts "#{txn['created_at']}: -#{txn['amount']} credits - #{txn['description']}"
endgo
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
type TransactionFilters struct {
Type string `json:"type,omitempty"`
StartDate string `json:"startDate,omitempty"`
EndDate string `json:"endDate,omitempty"`
Page int `json:"page,omitempty"`
Limit int `json:"limit,omitempty"`
}
func getTransactions(jwtToken string, filters TransactionFilters) (map[string]interface{}, error) {
baseURL := "https://api.yebolink.com/api/v1/billing/transactions"
// Build query parameters
params := url.Values{}
if filters.Type != "" {
params.Add("type", filters.Type)
}
if filters.StartDate != "" {
params.Add("startDate", filters.StartDate)
}
if filters.EndDate != "" {
params.Add("endDate", filters.EndDate)
}
if filters.Page > 0 {
params.Add("page", fmt.Sprintf("%d", filters.Page))
}
if filters.Limit > 0 {
params.Add("limit", fmt.Sprintf("%d", filters.Limit))
}
url := baseURL
if len(params) > 0 {
url += "?" + params.Encode()
}
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer "+jwtToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
return result, nil
}
// Usage
func main() {
// Get all transactions
allTxns, _ := getTransactions(jwtToken, TransactionFilters{})
// Get purchases only
purchases, _ := getTransactions(jwtToken, TransactionFilters{
Type: "purchase",
})
// Get usage for date range
usage, _ := getTransactions(jwtToken, TransactionFilters{
Type: "usage",
StartDate: "2025-10-01T00:00:00Z",
EndDate: "2025-10-31T23:59:59Z",
})
fmt.Println(usage)
}Response
json
{
"success": true,
"data": {
"transactions": [
{
"id": "txn_1234567890",
"type": "purchase",
"amount": 500,
"balance_after": 750,
"description": "Credit purchase - 500 credits",
"metadata": {
"package": "500",
"price": 45.00,
"stripe_session_id": "cs_test_..."
},
"created_at": "2025-11-02T10:00:00Z"
},
{
"id": "txn_0987654321",
"type": "usage",
"amount": -1,
"balance_after": 749,
"description": "SMS sent to +26878422613",
"metadata": {
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"channel": "sms",
"recipient": "+26878422613"
},
"created_at": "2025-11-02T11:30:00Z"
},
{
"id": "txn_1122334455",
"type": "refund",
"amount": 1,
"balance_after": 750,
"description": "Refund for failed SMS",
"metadata": {
"message_id": "660e8400-e29b-41d4-a716-446655440001",
"reason": "invalid_number"
},
"created_at": "2025-11-02T12:00:00Z"
}
],
"pagination": {
"total": 150,
"page": 1,
"pages": 3,
"limit": 50
}
}
}Transaction Types
Purchase
Credits added to account through payment.
json
{
"type": "purchase",
"amount": 500,
"description": "Credit purchase - 500 credits",
"metadata": {
"package": "500",
"price": 45.00,
"stripe_session_id": "cs_test_..."
}
}Usage
Credits deducted for sent messages.
json
{
"type": "usage",
"amount": -1,
"description": "SMS sent to +26878422613",
"metadata": {
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"channel": "sms",
"recipient": "+26878422613"
}
}Refund
Credits returned for failed messages.
json
{
"type": "refund",
"amount": 1,
"description": "Refund for failed SMS",
"metadata": {
"message_id": "660e8400-e29b-41d4-a716-446655440001",
"reason": "invalid_number"
}
}Adjustment
Manual credit adjustments by admin.
json
{
"type": "adjustment",
"amount": 100,
"description": "Promotional credits",
"metadata": {
"reason": "promotion",
"admin_id": "admin_123"
}
}Usage Analytics
Calculate Total Spending
javascript
async function calculateSpending(startDate, endDate) {
const response = await getTransactions({
type: 'purchase',
startDate,
endDate
});
const total = response.data.transactions.reduce(
(sum, txn) => sum + (txn.metadata?.price || 0),
0
);
return {
total: total.toFixed(2),
transactions: response.data.transactions.length,
averagePerPurchase: (total / response.data.transactions.length).toFixed(2)
};
}
// Usage
const spending = await calculateSpending(
'2025-10-01T00:00:00Z',
'2025-10-31T23:59:59Z'
);
console.log(`Total spent in October: $${spending.total}`);Track Credit Usage by Channel
javascript
async function analyzeUsageByChannel(startDate, endDate) {
const response = await getTransactions({
type: 'usage',
startDate,
endDate
});
const byChannel = response.data.transactions.reduce((acc, txn) => {
const channel = txn.metadata?.channel || 'unknown';
acc[channel] = (acc[channel] || 0) + Math.abs(txn.amount);
return acc;
}, {});
return byChannel;
}
// Usage
const usage = await analyzeUsageByChannel(
'2025-10-01T00:00:00Z',
'2025-10-31T23:59:59Z'
);
console.log('Credit usage by channel:');
console.log(`SMS: ${usage.sms || 0} credits`);
console.log(`WhatsApp: ${usage.whatsapp || 0} credits`);
console.log(`Email: ${usage.email || 0} credits`);Generate Monthly Report
javascript
async function generateMonthlyReport(year, month) {
const startDate = new Date(year, month - 1, 1).toISOString();
const endDate = new Date(year, month, 0, 23, 59, 59).toISOString();
// Get all transactions
const allTxns = await getTransactions({ startDate, endDate });
// Separate by type
const purchases = allTxns.data.transactions.filter(t => t.type === 'purchase');
const usage = allTxns.data.transactions.filter(t => t.type === 'usage');
const refunds = allTxns.data.transactions.filter(t => t.type === 'refund');
// Calculate totals
const totalSpent = purchases.reduce(
(sum, t) => sum + (t.metadata?.price || 0), 0
);
const totalCreditsUsed = Math.abs(
usage.reduce((sum, t) => sum + t.amount, 0)
);
const totalRefunds = refunds.reduce((sum, t) => sum + t.amount, 0);
return {
period: `${year}-${String(month).padStart(2, '0')}`,
purchases: {
count: purchases.length,
totalSpent: totalSpent.toFixed(2),
creditsPurchased: purchases.reduce((sum, t) => sum + t.amount, 0)
},
usage: {
count: usage.length,
creditsUsed: totalCreditsUsed,
byChannel: analyzeByChannel(usage)
},
refunds: {
count: refunds.length,
creditsRefunded: totalRefunds
}
};
}
// Usage
const report = await generateMonthlyReport(2025, 10);
console.log('Monthly Report:', JSON.stringify(report, null, 2));Export Transactions
Export to CSV
javascript
async function exportToCSV(startDate, endDate) {
let allTransactions = [];
let page = 1;
let hasMore = true;
// Fetch all pages
while (hasMore) {
const response = await getTransactions({
startDate,
endDate,
page,
limit: 100
});
allTransactions = allTransactions.concat(response.data.transactions);
hasMore = page < response.data.pagination.pages;
page++;
}
// Convert to CSV
const headers = ['Date', 'Type', 'Amount', 'Balance After', 'Description'];
const rows = allTransactions.map(txn => [
new Date(txn.created_at).toLocaleString(),
txn.type,
txn.amount,
txn.balance_after,
txn.description
]);
const csv = [headers, ...rows]
.map(row => row.join(','))
.join('\n');
return csv;
}
// Usage
const csv = await exportToCSV(
'2025-10-01T00:00:00Z',
'2025-10-31T23:59:59Z'
);
// Download as file
const blob = new Blob([csv], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'transactions.csv';
a.click();Export to JSON
javascript
async function exportToJSON(startDate, endDate) {
const response = await getTransactions({ startDate, endDate, limit: 100 });
const exportData = {
exported_at: new Date().toISOString(),
period: {
start: startDate,
end: endDate
},
transactions: response.data.transactions
};
return JSON.stringify(exportData, null, 2);
}Real-Time Balance Tracking
javascript
// Track balance changes in real-time
class BalanceTracker {
constructor(jwtToken) {
this.jwtToken = jwtToken;
this.currentBalance = null;
this.listeners = [];
}
async initialize() {
// Get initial balance
const loginResponse = await this.refreshBalance();
this.currentBalance = loginResponse.data.workspace.credits_balance;
return this.currentBalance;
}
async refreshBalance() {
// You would implement this based on your auth endpoint
const response = await fetch('https://api.yebolink.com/api/v1/auth/me', {
headers: { 'Authorization': `Bearer ${this.jwtToken}` }
});
return await response.json();
}
subscribe(callback) {
this.listeners.push(callback);
}
async checkForChanges() {
const latest = await this.refreshBalance();
const newBalance = latest.data.workspace.credits_balance;
if (newBalance !== this.currentBalance) {
const change = newBalance - this.currentBalance;
this.currentBalance = newBalance;
this.listeners.forEach(callback => {
callback({
balance: newBalance,
change,
timestamp: new Date()
});
});
}
}
startPolling(intervalMs = 30000) {
return setInterval(() => this.checkForChanges(), intervalMs);
}
}
// Usage
const tracker = new BalanceTracker(jwtToken);
await tracker.initialize();
tracker.subscribe(({ balance, change, timestamp }) => {
console.log(`Balance updated: ${balance} (${change > 0 ? '+' : ''}${change})`);
if (change > 0) {
console.log('Credits added!');
} else if (change < 0) {
console.log('Credits used');
}
});
// Check every 30 seconds
tracker.startPolling(30000);Best Practices
1. Regular Audits
javascript
// Run monthly audits
async function monthlyAudit() {
const now = new Date();
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
const report = await generateMonthlyReport(
now.getFullYear(),
now.getMonth() + 1
);
// Alert if spending is high
if (parseFloat(report.purchases.totalSpent) > 500) {
console.warn('⚠️ High spending this month:', report.purchases.totalSpent);
}
// Alert if refunds are high
if (report.refunds.count > 10) {
console.warn('⚠️ High refund count:', report.refunds.count);
}
return report;
}2. Set Spending Alerts
javascript
async function checkSpendingLimit(monthlyLimit) {
const now = new Date();
const spending = await calculateSpending(
new Date(now.getFullYear(), now.getMonth(), 1).toISOString(),
now.toISOString()
);
if (parseFloat(spending.total) > monthlyLimit) {
// Send alert email or notification
console.error(`Monthly limit exceeded! Spent: $${spending.total}, Limit: $${monthlyLimit}`);
return false;
}
return true;
}3. Monitor Refund Rate
javascript
async function calculateRefundRate(startDate, endDate) {
const usage = await getTransactions({ type: 'usage', startDate, endDate });
const refunds = await getTransactions({ type: 'refund', startDate, endDate });
const totalUsage = Math.abs(
usage.data.transactions.reduce((sum, t) => sum + t.amount, 0)
);
const totalRefunds = refunds.data.transactions.reduce(
(sum, t) => sum + t.amount, 0
);
const refundRate = (totalRefunds / totalUsage) * 100;
if (refundRate > 5) {
console.warn(`⚠️ High refund rate: ${refundRate.toFixed(2)}%`);
}
return refundRate.toFixed(2);
}Need Help?
- Billing Overview - Understanding credits
- Purchase Credits - Buy more credits
- API Reference - Complete API docs
- Email: support@yebolink.app