Overview
ActumX uses a credit-based prepaid billing system . You add credits to your account, then credits are automatically deducted as you use paid API endpoints.
Key Concepts
Credits : Prepaid balance stored in cents (USD)
Payment Intents : Top-up transactions that add credits
Credit Ledger : Double-entry accounting log of all credits/debits
Usage Events : Records of API usage and costs
x402 Transactions : Payment records for paid requests
All monetary values in ActumX are stored in cents (USD) to avoid floating-point precision issues.
Credit-Based Model
How It Works
Top Up : Add credits to your account via payment intent
Use API : Make requests to paid endpoints (e.g., x402 protected routes)
Auto Deduct : Credits are automatically deducted when you settle x402 payments
Track Usage : View your balance, top-ups, and usage in real-time
Balance Calculation
Your balance is computed from the credit ledger:
// Balance = Sum(credits) - Sum(debits)
const balanceCents =
totalTopUps - totalUsage ;
Source Reference : /home/daytona/workspace/source/api/src/services/credits.service.ts (inferred from usage in billing/service.ts:88)
Payment Intents
Payment intents represent top-up transactions.
Payment Intent Structure
interface PaymentIntent {
id : string ; // Unique intent ID (prefixed with "pi_")
userId : string ; // Your user ID
amountCents : number ; // Top-up amount in cents
status : string ; // "pending" | "settled" | "failed"
providerReference : string | null ; // External payment provider ref
createdAt : string ; // ISO 8601 timestamp
updatedAt : string ; // ISO 8601 timestamp
}
Database Schema
CREATE TABLE payment_intents (
id TEXT PRIMARY KEY ,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE ,
amount_cents INTEGER NOT NULL ,
status TEXT NOT NULL ,
provider_reference TEXT ,
created_at TEXT NOT NULL ,
updated_at TEXT NOT NULL
);
CREATE INDEX idx_payment_intents_user_id ON payment_intents(user_id);
Source Reference : /home/daytona/workspace/source/api/src/db/schema.ts:18-28
Creating a Payment Intent
Top up your account:
curl -X POST https://api.actumx.com/api/v1/billing/top-up \
-H "Cookie: your_session_cookie" \
-H "Content-Type: application/json" \
-d '{
"amountCents": 1000
}'
Request Constraints:
Minimum: 100 cents ($1.00 USD)
Maximum: 100,000 cents ($1,000.00 USD)
Response:
{
"paymentIntentId" : "pi_abc123" ,
"addedCents" : 1000 ,
"balanceCents" : 1500
}
Currently, ActumX uses a dummy payment provider for development. In production, this would integrate with Stripe, PayPal, or similar.
Payment Intent Flow
// Create payment intent (source: service.ts:68-76)
await db . insert ( paymentIntents ). values ({
id: newId ( "pi" ),
userId: auth . user . id ,
amountCents: payload . amountCents ,
status: "settled" , // Auto-settled in dev mode
providerReference: `dummy_ ${ newId ( "provider" ) } ` ,
createdAt: timestamp ,
updatedAt: timestamp ,
});
// Add credit to ledger (source: service.ts:78-86)
await db . insert ( creditLedger ). values ({
id: newId ( "ledger" ),
userId: auth . user . id ,
direction: "credit" ,
amountCents: payload . amountCents ,
source: "top_up" ,
referenceId: intentId ,
createdAt: timestamp ,
});
Listing Payment Intents
View your top-up history:
curl https://api.actumx.com/api/v1/billing/payment-intents \
-H "Cookie: your_session_cookie"
Response:
{
"intents" : [
{
"id" : "pi_abc123" ,
"userId" : "user_xyz" ,
"amountCents" : 1000 ,
"status" : "settled" ,
"providerReference" : "dummy_provider_abc" ,
"createdAt" : "2026-03-03T20:00:00Z" ,
"updatedAt" : "2026-03-03T20:00:01Z"
}
]
}
Intents are sorted by creation date (newest first), limited to 50 results.
Credit Ledger
The credit ledger is a double-entry accounting log of all balance changes.
Ledger Entry Structure
interface LedgerEntry {
id : string ; // Unique ledger entry ID (prefixed with "ledger_")
userId : string ; // Your user ID
direction : "credit" | "debit" ; // Credit (add) or debit (subtract)
amountCents : number ; // Amount changed in cents
source : string ; // "top_up" | "api_request"
referenceId : string | null ; // ID of related payment intent or x402 tx
createdAt : string ; // ISO 8601 timestamp
}
Database Schema
CREATE TABLE credit_ledger (
id TEXT PRIMARY KEY ,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE ,
direction TEXT NOT NULL ,
amount_cents INTEGER NOT NULL ,
source TEXT NOT NULL ,
reference_id TEXT ,
created_at TEXT NOT NULL
);
CREATE INDEX idx_credit_ledger_user_id ON credit_ledger(user_id);
Source Reference : /home/daytona/workspace/source/api/src/db/schema.ts:30-40
Ledger Entry Types
Credit Entry (Top-Up)
{
"id" : "ledger_abc123" ,
"userId" : "user_xyz" ,
"direction" : "credit" ,
"amountCents" : 1000 ,
"source" : "top_up" ,
"referenceId" : "pi_abc123" , // Payment intent ID
"createdAt" : "2026-03-03T20:00:00Z"
}
Debit Entry (API Usage)
{
"id" : "ledger_xyz789" ,
"userId" : "user_xyz" ,
"direction" : "debit" ,
"amountCents" : 10 ,
"source" : "api_request" ,
"referenceId" : "x402tx_abc" , // x402 transaction ID
"createdAt" : "2026-03-03T22:30:00Z"
}
Balance Calculation
Your current balance is computed by summing all ledger entries:
// Conceptual balance calculation
const credits = await db
. select ({ sum: sql `SUM(amount_cents)` })
. from ( creditLedger )
. where ( and (
eq ( creditLedger . userId , userId ),
eq ( creditLedger . direction , "credit" )
));
const debits = await db
. select ({ sum: sql `SUM(amount_cents)` })
. from ( creditLedger )
. where ( and (
eq ( creditLedger . userId , userId ),
eq ( creditLedger . direction , "debit" )
));
const balanceCents = ( credits . sum || 0 ) - ( debits . sum || 0 );
The ledger provides an immutable audit trail of all financial transactions.
Usage Events
Usage events record each API request and its cost.
Usage Event Structure
interface UsageEvent {
id : string ; // Unique usage ID (prefixed with "usage_")
userId : string ; // Your user ID
apiKeyId : string ; // API key used for request
endpoint : string ; // API endpoint path
method : string ; // HTTP method (GET, POST, etc.)
units : number ; // Number of units consumed (e.g., 1 request)
costCents : number ; // Cost in cents
x402TransactionId : string ; // Related x402 transaction
createdAt : string ; // ISO 8601 timestamp
}
Database Schema
CREATE TABLE usage_events (
id TEXT PRIMARY KEY ,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE ,
api_key_id TEXT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE ,
endpoint TEXT NOT NULL ,
method TEXT NOT NULL ,
units INTEGER NOT NULL ,
cost_cents INTEGER NOT NULL ,
x402_transaction_id TEXT NOT NULL REFERENCES x402_transactions(id) ON DELETE CASCADE ,
created_at TEXT NOT NULL
);
CREATE INDEX idx_usage_events_user_id ON usage_events(user_id);
Source Reference : /home/daytona/workspace/source/api/src/db/schema.ts:60-72
Usage Tracking
Usage events are created when x402 payments are consumed:
// Track usage after payment consumption (source: x402/service.ts:412-422)
await db . insert ( usageEvents ). values ({
id: newId ( "usage" ),
userId: apiKey . userId ,
apiKeyId: apiKey . id ,
endpoint: X402_PAID_ENDPOINT ,
method: "GET" ,
units: 1 ,
costCents: transaction . amountCents ,
x402TransactionId: transaction . id ,
createdAt: timestamp ,
});
This enables:
Detailed usage analytics
Cost attribution per API key
Usage trends over time
Billing dispute resolution
Billing Summary
Get an overview of your billing status:
curl https://api.actumx.com/api/v1/billing/summary \
-H "Cookie: your_session_cookie"
Response:
{
"balanceCents" : 1490 ,
"topUpTotalCents" : 1500 ,
"usageTotalCents" : 10 ,
"activeApiKeys" : 2 ,
"x402Transactions" : 1
}
Summary Calculations
// Top-up total (source: service.ts:18-21)
const [ topUpTotal ] = await db
. select ({ totalCents: sql `COALESCE(SUM( ${ paymentIntents . amountCents } ), 0)` })
. from ( paymentIntents )
. where ( and (
eq ( paymentIntents . userId , auth . user . id ),
eq ( paymentIntents . status , "settled" )
));
// Usage total (source: service.ts:23-26)
const [ usageTotal ] = await db
. select ({ totalCents: sql `COALESCE(SUM( ${ usageEvents . costCents } ), 0)` })
. from ( usageEvents )
. where ( eq ( usageEvents . userId , auth . user . id ));
// Active API keys (source: service.ts:28-31)
const [ activeKeyCount ] = await db
. select ({ count: sql `COUNT(*)` })
. from ( apiKeys )
. where ( and (
eq ( apiKeys . userId , auth . user . id ),
isNull ( apiKeys . revokedAt )
));
// x402 transaction count (source: service.ts:33-36)
const [ transactionCount ] = await db
. select ({ count: sql `COUNT(*)` })
. from ( x402Transactions )
. where ( eq ( x402Transactions . userId , auth . user . id ));
Pricing
Current Costs
x402 Quote Request : 10 cents ($0.10 USD)
Pricing is configured in /home/daytona/workspace/source/api/src/config/constants.ts via X402_PAID_REQUEST_COST_CENTS.
Future Pricing Model
ActumX may introduce tiered pricing:
Tier Monthly Requests Cost per Request Free 100 $0.00 Starter 1,000 $0.08 Pro 10,000 $0.05 Enterprise Unlimited Custom
Payment Flow Diagram
┌─────────────┐
│ Top Up │
│ (User) │
└──────┬──────┘
│
▼
┌──────────────────┐ ┌──────────────────┐
│ Payment Intent │─────▶│ Credit Ledger │
│ (settled) │ │ (credit entry) │
└──────────────────┘ └──────────────────┘
│
│
┌────────▼─────────┐
│ Balance Update │
└────────┬─────────┘
│
┌───────────────────────────┘
│
▼
┌──────────────────┐
│ API Request │
│ (x402 flow) │
└──────┬───────────┘
│
▼
┌──────────────────┐ ┌──────────────────┐
│ x402 Settle │─────▶│ Credit Ledger │
│ │ │ (debit entry) │
└──────┬───────────┘ └──────────────────┘
│ │
│ │
▼ ┌────────▼─────────┐
┌──────────────────┐ │ Balance Update │
│ Usage Event │ └──────────────────┘
│ (recorded) │
└──────────────────┘
Implementation Details
Service Layer
The billing service handles all financial operations:
Key Functions:
summary(request): Get billing overview
topUp(request, payload): Add credits to account
paymentIntents(request): List top-up history
Source Reference : /home/daytona/workspace/source/api/src/modules/billing/service.ts
Validation
Top-up amounts are validated:
// Model validation (source: model.ts:4-6)
const topUpBody = t . Object ({
amountCents: t . Number ({ minimum: 100 }),
});
// Additional server-side check (source: service.ts:58-62)
if ( payload . amountCents < 100 || payload . amountCents > 100000 ) {
return {
statusCode: 400 ,
body: { error: "amount_must_be_between_100_and_100000_cents" },
};
}
Credits Service
The CreditsService computes balances:
// Compute current balance
const balanceCents = await CreditsService . computeBalanceCents ( userId );
Source Reference : /home/daytona/workspace/source/api/src/services/credits.service.ts (inferred)
Best Practices
1. Monitor Your Balance
Regularly check your balance to avoid service interruptions:
curl https://api.actumx.com/api/v1/billing/summary
2. Set Balance Alerts
Implement client-side alerts when balance drops below a threshold:
const { balanceCents } = await actumx . get ( '/billing/summary' );
if ( balanceCents < 500 ) { // Less than $5
console . warn ( 'Low balance! Top up soon.' );
sendEmailAlert ();
}
3. Track Usage by API Key
Use different API keys for different services to track costs:
# Create separate keys
curl -X POST /api/v1/api-keys -d '{"name": "Production Bot"}'
curl -X POST /api/v1/api-keys -d '{"name": "Staging Tests"}'
Usage events will show which key incurred costs.
4. Automate Top-Ups
Set up automatic top-ups when balance is low:
if ( balanceCents < 500 ) {
await actumx . post ( '/billing/top-up' , {
amountCents: 1000 // Add $10
});
}
5. Review Usage Events
Periodically audit usage events for unexpected costs:
-- Query usage events (conceptual)
SELECT
endpoint ,
COUNT ( * ) as request_count,
SUM (cost_cents) as total_cost
FROM usage_events
WHERE user_id = 'your_user_id'
GROUP BY endpoint ;
Error Handling
Common billing errors:
Error HTTP Status Cause Solution unauthorized401 Missing auth Provide session cookie insufficient_balance402 Not enough credits Top up account amount_must_be_between_100_and_100000_cents400 Invalid top-up Use 1 − 1- 1 − 1000 range payment_not_settled402 Payment pending Wait for settlement
Example Error Response
{
"error" : "insufficient_balance" ,
"requiredCents" : 10 ,
"balanceCents" : 5 ,
"message" : "Top up balance in dashboard before settling this x402 payment."
}
Security Considerations
Transaction Atomicity : All ledger entries are created in database transactions
Idempotency : Payment intents use unique IDs to prevent double-charging
User Isolation : All queries filter by userId to prevent cross-user access
Audit Trail : The ledger provides immutable financial history
Balance Validation : Settlement checks balance before debiting
Next Steps
Billing & Credits Guide Step-by-step guide to managing your credits
Dashboard - Billing View billing details in the web dashboard
API Reference View detailed billing API endpoints
x402 Protocol Learn how x402 payments deduct credits