GraphQL Credit Management
This page documents GraphQL operations for managing API credits, including checking balances, viewing packages, and purchasing additional credits.
Credit System Overview
Crane Ledger uses a credit-based billing system where API calls consume credits. Organizations can:
- Check their current credit balance
- View available credit packages
- Purchase additional credits via Stripe checkout
- Monitor credit usage and thresholds
Credit Balance Queries
Get Credit Balance
Check your organization's current credit status.
query {
creditBalance {
organizationId
creditsRemaining
creditsTotal
autoRechargeEnabled
lowBalanceThreshold
}
}
Returns: CreditBalanceNode!
Access: Current organization's API key required
Response Example:
{
"data": {
"creditBalance": {
"organizationId": "ORG_12345678901234567890123456789012",
"creditsRemaining": 1250,
"creditsTotal": 5000,
"autoRechargeEnabled": false,
"lowBalanceThreshold": 100
}
}
}
Real-time Balance Checking
// Check balance before making expensive operations
async function checkBalance() {
const query = `
query {
creditBalance {
creditsRemaining
lowBalanceThreshold
}
}
`;
const response = await graphql.request(query);
const balance = response.data.creditBalance;
if (balance.creditsRemaining <= balance.lowBalanceThreshold) {
showLowBalanceWarning();
}
return balance.creditsRemaining;
}
Credit Packages
View Available Packages
Get all available credit packages for purchase.
query {
creditPackages {
packageType
credits
name
priceUsd
priceCents
}
}
Returns: [CreditPackageNode!]!
Access: Public (same packages for all organizations)
Response Example:
{
"data": {
"creditPackages": [
{
"packageType": "SOLO_DEV",
"credits": 1000,
"name": "Solo Dev - 1,000 credits",
"priceUsd": 10.0,
"priceCents": 1000
},
{
"packageType": "BUILDER",
"credits": 5000,
"name": "Builder - 5,000 credits",
"priceUsd": 50.0,
"priceCents": 5000
},
{
"packageType": "SCALE",
"credits": 25000,
"name": "Scale - 25,000 credits",
"priceUsd": 250.0,
"priceCents": 25000
}
]
}
}
Package Details
SOLO_DEV Package:
- Credits: 1,000
- Price: $10.00 ($0.01 per credit)
- Best for: Individual developers, small projects
BUILDER Package:
- Credits: 5,000
- Price: $50.00 ($0.01 per credit)
- Best for: Development teams, moderate usage
SCALE Package:
- Credits: 25,000
- Price: $250.00 ($0.01 per credit)
- Best for: High-volume applications, production systems
Credit Purchase
Initiate Credit Purchase
Start the credit purchase process via Stripe checkout.
mutation PurchaseCredits(
$creditAmount: Int!
$successUrl: String
$cancelUrl: String
) {
purchaseCredits(
creditAmount: $creditAmount
successUrl: $successUrl
cancelUrl: $cancelUrl
) {
checkoutUrl
creditsToAdd
priceCents
expiresAt
}
}
Required Arguments:
creditAmount: Int!- Number of credits to purchase (must match available packages)
Optional Arguments:
successUrl: String- URL to redirect after successful paymentcancelUrl: String- URL to redirect if payment is cancelled
Returns: CreditPurchaseNode!
Process Flow:
- Mutation validates credit amount and organization
- Creates Stripe checkout session
- Returns checkout URL for payment completion
- User completes payment on Stripe
- Stripe webhooks credit the organization
- Credits become available immediately
Purchase Examples
Purchase Solo Dev Package
const purchaseSoloDev = async () => {
const mutation = `
mutation PurchaseCredits($creditAmount: Int!) {
purchaseCredits(creditAmount: $creditAmount) {
checkoutUrl
creditsToAdd
priceCents
}
}
`;
const response = await graphql.request(mutation, {
creditAmount: 1000
});
// Redirect user to Stripe checkout
window.location.href = response.data.purchaseCredits.checkoutUrl;
};
Custom Amount Purchase
const purchaseCustomCredits = async (amount) => {
const mutation = `
mutation PurchaseCredits($creditAmount: Int!, $successUrl: String) {
purchaseCredits(
creditAmount: $creditAmount
successUrl: $successUrl
) {
checkoutUrl
creditsToAdd
priceCents
expiresAt
}
}
`;
const response = await graphql.request(mutation, {
creditAmount: amount,
successUrl: `${window.location.origin}/credits/purchased`
});
return response.data.purchaseCredits;
};
Handling Purchase Responses
const handleCreditPurchase = async (creditAmount) => {
try {
const result = await purchaseCredits(creditAmount);
// Store checkout session info for tracking
sessionStorage.setItem('checkout_session', JSON.stringify({
creditsToAdd: result.creditsToAdd,
expiresAt: result.expiresAt
}));
// Redirect to Stripe checkout
window.location.href = result.checkoutUrl;
} catch (error) {
if (error.extensions?.code === 'VALIDATION_ERROR') {
showError('Invalid credit amount. Please select a valid package.');
} else {
showError('Failed to initiate purchase. Please try again.');
}
}
};
Credit Usage Monitoring
Balance Monitoring
Monitor credit balance and usage patterns.
class CreditMonitor {
constructor() {
this.balance = null;
this.threshold = 100; // Alert when below 100 credits
}
async checkBalance() {
const query = `
query {
creditBalance {
creditsRemaining
lowBalanceThreshold
}
}
`;
const response = await graphql.request(query);
this.balance = response.data.creditBalance;
this.checkThresholds();
return this.balance;
}
checkThresholds() {
if (this.balance.creditsRemaining <= this.balance.lowBalanceThreshold) {
this.showLowBalanceAlert();
}
if (this.balance.creditsRemaining <= 50) {
this.showCriticalBalanceAlert();
}
}
showLowBalanceAlert() {
showNotification({
type: 'warning',
title: 'Low Credit Balance',
message: `You have ${this.balance.creditsRemaining} credits remaining. Consider purchasing more.`,
action: {
text: 'Buy Credits',
onClick: () => navigateToCreditsPage()
}
});
}
showCriticalBalanceAlert() {
showNotification({
type: 'error',
title: 'Critical Credit Balance',
message: `Only ${this.balance.creditsRemaining} credits remaining. API calls may fail.`,
action: {
text: 'Buy Credits Now',
onClick: () => navigateToCreditsPage()
}
});
}
}
Usage Tracking
Track credit consumption patterns.
const trackCreditUsage = async () => {
// Get initial balance
const initialBalance = await getCreditBalance();
// Perform operations
await performOperations();
// Check final balance
const finalBalance = await getCreditBalance();
const creditsUsed = initialBalance.creditsRemaining - finalBalance.creditsRemaining;
// Log usage
analytics.track('credit_usage', {
credits_used: creditsUsed,
operations_performed: operationCount,
average_cost_per_operation: creditsUsed / operationCount
});
return creditsUsed;
};
Credit Purchase Integration
React Component Example
function CreditPurchase({ onPurchaseComplete }) {
const [packages, setPackages] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
loadCreditPackages();
}, []);
const loadCreditPackages = async () => {
const query = `
query {
creditPackages {
packageType
credits
name
priceUsd
}
}
`;
const response = await graphql.request(query);
setPackages(response.data.creditPackages);
};
const purchasePackage = async (creditAmount) => {
setLoading(true);
try {
const mutation = `
mutation PurchaseCredits($creditAmount: Int!) {
purchaseCredits(creditAmount: $creditAmount) {
checkoutUrl
}
}
`;
const response = await graphql.request(mutation, { creditAmount });
// Redirect to Stripe checkout
window.location.href = response.data.purchaseCredits.checkoutUrl;
} catch (error) {
showError('Purchase failed: ' + error.message);
} finally {
setLoading(false);
}
};
return (
<div className="credit-packages">
<h2>Purchase Credits</h2>
{packages.map(pkg => (
<div key={pkg.packageType} className="package-card">
<h3>{pkg.name}</h3>
<p>{pkg.credits} credits</p>
<p>${pkg.priceUsd}</p>
<button
onClick={() => purchasePackage(pkg.credits)}
disabled={loading}
>
{loading ? 'Processing...' : 'Purchase'}
</button>
</div>
))}
</div>
);
}
Success/Cancel Handling
// Handle successful purchase return
function handlePurchaseSuccess(sessionId) {
// Verify purchase with backend
verifyPurchase(sessionId).then(() => {
showSuccess('Credits purchased successfully!');
refreshCreditBalance();
onPurchaseComplete?.();
});
}
// Handle cancelled purchase
function handlePurchaseCancel() {
showInfo('Purchase cancelled. No credits were charged.');
}
Credit Cost Estimation
Estimate Operation Costs
Calculate expected credit costs before operations.
const estimateCreditCost = (operations) => {
const costEstimates = {
// Basic operations
create_account: 1,
update_account: 1,
delete_account: 1,
// Transaction operations
create_transaction: 2,
get_transaction: 1,
// Report operations
generate_trial_balance: 5,
generate_balance_sheet: 10,
generate_income_statement: 10,
// Document operations
create_invoice: 3,
create_bill: 3,
get_invoice_pdf: 2,
};
return operations.reduce((total, op) => {
return total + (costEstimates[op.type] || 1);
}, 0);
};
// Usage
const operations = [
{ type: 'create_account' },
{ type: 'create_transaction' },
{ type: 'generate_trial_balance' }
];
const estimatedCost = estimateCreditCost(operations);
// Result: 8 credits
Budget Management
Implement spending limits and alerts.
class CreditBudget {
constructor(maxSpendPerHour = 100) {
this.maxSpendPerHour = maxSpendPerHour;
this.hourlySpend = 0;
this.lastReset = Date.now();
}
async checkBudget(operationCost) {
this.resetIfNeeded();
if (this.hourlySpend + operationCost > this.maxSpendPerHour) {
throw new Error(`Budget exceeded. Would spend ${this.hourlySpend + operationCost} credits this hour.`);
}
return true;
}
recordSpend(amount) {
this.hourlySpend += amount;
}
resetIfNeeded() {
const now = Date.now();
const hourInMs = 60 * 60 * 1000;
if (now - this.lastReset >= hourInMs) {
this.hourlySpend = 0;
this.lastReset = now;
}
}
}
Error Handling
Purchase Errors
Common credit purchase error codes:
VALIDATION_ERROR- Invalid credit amount or parametersINTERNAL_ERROR- Stripe configuration or API issuesFORBIDDEN- Organization not authorized for purchases
Balance Errors
INTERNAL_ERROR- Unable to retrieve balanceNOT_FOUND- Organization not found
Handling Errors
const handleCreditError = (error) => {
switch (error.extensions?.code) {
case 'VALIDATION_ERROR':
showError('Invalid purchase amount. Please select a valid credit package.');
break;
case 'INTERNAL_ERROR':
showError('Payment system temporarily unavailable. Please try again later.');
break;
case 'RATE_LIMIT_EXCEEDED':
showError('Too many requests. Please wait a moment before trying again.');
break;
default:
showError('An unexpected error occurred. Please contact support.');
}
};
Best Practices
Credit Management
- Monitor Balance Regularly - Check balance before critical operations
- Set Low Balance Alerts - Configure notifications for low credit warnings
- Purchase in Advance - Buy credits before running out to avoid service interruption
- Track Usage Patterns - Monitor which operations consume the most credits
- Implement Budget Controls - Set spending limits for different environments
Purchase Process
- Validate Amounts - Ensure purchase amounts match available packages
- Handle Redirects - Implement proper success/cancel URL handling
- Verify Purchases - Confirm credits were added after successful payment
- Store Session Info - Track checkout sessions for debugging
- Error Recovery - Handle failed payments gracefully
Cost Optimization
- Batch Operations - Combine multiple operations in single requests
- Use Pagination - Limit result sets to reduce credit consumption
- Cache Results - Avoid repeated expensive queries
- Select Fields - Only request needed fields in GraphQL queries
- Monitor Usage - Track and analyze credit consumption patterns
Support and Troubleshooting
Common Issues
Purchase Not Completing:
- Check Stripe checkout session hasn't expired
- Verify payment method was accepted
- Contact support with session ID
Credits Not Added:
- Wait a few minutes for webhook processing
- Check organization ID matches purchase
- Contact support with transaction details
Balance Not Updating:
- Refresh balance query
- Check for cached results
- Contact support if issue persists
Getting Help
For credit-related issues:
- Check the GraphQL error response for specific error codes
- Include organization ID and transaction details when contacting support
- Provide timestamps and operation details for debugging
Need help?
Create a free account to access our support portal. Once signed in, use the Support tab in your dashboard to submit a support ticket — our team typically responds within 24 hours.
- ✨ For LLMs/AI assistants: Read our structured API reference