Manage Customer Balances
Customer balances are prepaid accounts that are automatically debited as usage ticks are reported. Top up balances via API, payment checkout, or external payment systems.
Top Up Balance with Payment (Recommended)
Create a charge and redirect customers to checkout. When payment succeeds, the balance is automatically topped up.
Endpoint
/v1/metered-billing/balances/top-up-with-paymentAuthorization: Bearer <ORVION_API_KEY> or X-API-Key: <ORVION_API_KEY>
Content-Type: application/json
Request Body
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| customer_ref | string | Required | Your internal customer identifier | user_123 |
| currency | string | Required | ISO currency code (3 characters) | USD |
| amount | string | Required | Amount to add as a decimal string | 100.00 |
| return_url | string | Required | URL to redirect customer after payment succeeds | https://yourapp.com/dashboard |
| description | string | null | Optional | Description for this top-up | Monthly prepayment |
| metadata | object | null | Optional | Free-form JSON object stored with the transaction | {"source": "dashboard"} |
| receiver_config_id | string | null | Optional | Optional receiver config ID for payment routing | config_abc123 |
| flow_slug | string | null | Optional | Optional billing flow slug for payment routing | mainnet-usdc |
Response
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| charge_id | string | Optional | Charge/transaction ID | txn_abc123 |
| checkout_url | string | Optional | Checkout URL to redirect customer | https://pay.orvion.sh/checkout/txn_abc123 |
| amount | string | Optional | Amount to be charged | 100.00 |
| currency | string | Optional | Currency code | USD |
| customer_ref | string | Optional | Customer reference | user_123 |
Example: cURL
curl -X POST "https://api.orvion.sh/v1/metered-billing/balances/top-up-with-payment" \-H "Authorization: Bearer $ORVION_API_KEY" \-H "Content-Type: application/json" \-d '{"customer_ref": "user_123","currency": "USD","amount": "100.00","return_url": "https://yourapp.com/dashboard","description": "Monthly prepayment"}'
Example: Python SDK
from orvion import OrvionClient# Initialize clientorvion = OrvionClient(api_key="your_api_key")# Top up balance with paymentresult = await orvion.metered_billing.top_up_balance_with_payment(customer_ref="user_123",currency="USD",amount="100.00",return_url="https://yourapp.com/dashboard",description="Monthly prepayment")print(f"Charge ID: {result.charge_id}")print(f"Checkout URL: {result.checkout_url}")# Redirect customer to checkout_url
Example: Node.js SDK
import { OrvionClient } from '@orvion/sdk';// Initialize clientconst orvion = new OrvionClient({ apiKey: 'your_api_key' });// Top up balance with paymentconst result = await orvion.meteredBilling.topUpBalanceWithPayment({customerRef: 'user_123',currency: 'USD',amount: '100.00',returnUrl: 'https://yourapp.com/dashboard',description: 'Monthly prepayment'});console.log(`Charge ID: ${result.chargeId}`);console.log(`Checkout URL: ${result.checkoutUrl}`);// Redirect customer to checkoutUrl
Response Example
{
"charge_id": "txn_abc123",
"checkout_url": "https://pay.orvion.sh/checkout/txn_abc123",
"amount": "100.00",
"currency": "USD",
"customer_ref": "user_123"
}
Payment Flow
1. Create Top-Up Charge 2. Redirect to Checkout 3. Customer Pays
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ POST /top-up-with- │ │ Redirect to │ │ Customer connects │
│ payment │ ──▶ │ checkout_url │ ──▶ │ wallet and pays │
│ │ │ │ │ │
│ Returns: │ │ pay.orvion.sh/ │ │ Transaction │
│ - charge_id │ │ checkout/txn_abc123 │ │ confirmed on-chain │
│ - checkout_url │ │ │ │ │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
│
▼
4. Payment Confirmed 5. Auto Top-Up Balance 6. Redirect Back
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ Webhook: │ │ Balance topped up │ │ Customer redirected │
│ billing.transaction.│ ──▶ │ automatically │ ──▶ │ to return_url │
│ succeeded │ │ │ │ │
│ │ │ Balance: $100.00 │ │ ?charge_id=txn_abc │
│ Metadata: │ │ │ │ &status=succeeded │
│ purpose: balance_ │ │ │ │ │
│ topup │ │ │ │ │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
Automatic Balance Top-Up
When payment succeeds, Orvion automatically:
- Detects the transaction has
metadata.purpose: "balance_topup" - Extracts
customer_refandbalance_currencyfrom metadata - Tops up the customer's balance with the payment amount
- Creates a ledger entry linking the transaction to the balance
No webhook handler needed - it's handled automatically by Orvion!
Top Up Balance (Manual)
Add funds to a customer's balance directly (for manual credits, refunds, or external payments).
Endpoint
/v1/metered-billing/balances/top-upAuthorization: Bearer <ORVION_API_KEY> or X-API-Key: <ORVION_API_KEY>
Content-Type: application/json
Request Body
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| customer_ref | string | Required | Your internal customer identifier | user_123 |
| currency | string | Required | ISO currency code (3 characters) | USD |
| amount | string | Required | Amount to add as a decimal string | 100.00 |
| description | string | null | Optional | Description for this top-up | Monthly prepayment |
| metadata | object | null | Optional | Free-form JSON object stored with the ledger entry | {"payment_id": "pay_123", "source": "stripe"} |
Example: cURL
curl -X POST "https://api.orvion.sh/v1/metered-billing/balances/top-up" \-H "Authorization: Bearer $ORVION_API_KEY" \-H "Content-Type: application/json" \-d '{"customer_ref": "user_123","currency": "USD","amount": "100.00","description": "Monthly prepayment","metadata": {"payment_id": "pay_123","source": "stripe"}}'
Example: Python SDK
from orvion import OrvionClient# Initialize clientorvion = OrvionClient(api_key="your_api_key")# Top up balancebalance = await orvion.metered_billing.top_up_balance(customer_ref="user_123",currency="USD",amount="100.00",description="Monthly prepayment",metadata={"payment_id": "pay_123","source": "stripe"})print(f"Balance ID: {balance.id}")print(f"Available: ${balance.available_amount}")
Example: Node.js SDK
import { OrvionClient } from '@orvion/sdk';// Initialize clientconst orvion = new OrvionClient({ apiKey: 'your_api_key' });// Top up balanceconst balance = await orvion.meteredBilling.topUpBalance({customerRef: 'user_123',currency: 'USD',amount: '100.00',description: 'Monthly prepayment',metadata: {payment_id: 'pay_123',source: 'stripe'}});console.log(`Balance ID: ${balance.id}`);console.log(`Available: $${balance.availableAmount}`);
Response Example
{
"id": "bal_abc123",
"organization_id": "org_xyz",
"customer_ref": "user_123",
"currency": "USD",
"available_amount": "100.00",
"low_balance_threshold": null,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z"
}
Public Balance Top-Up (For Consumers)
Allow consumers who are not in the Orvion ecosystem to top up their balances directly without requiring API keys or authentication. This is perfect for end-users who only know their customer_ref.
Endpoint
/v1/top-up/{customer_ref}Authentication: None (public endpoint)
Content-Type: application/json
Request Body
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| amount | string | Required | Amount to top up as decimal string | 100.00 |
| currency | string | Optional | Currency code (default: USD) | USD |
| organization_id | string | null | Optional | Organization ID to use when multiple merchants have the same customer_ref. Required if multiple matches found. | org_abc123 |
| return_url | string | null | Optional | URL to redirect after payment (defaults to top-up page) | https://yourapp.com/balance |
| description | string | null | Optional | Optional description for the top-up | Monthly prepayment |
Example: cURL
# Top up balance for a customercurl -X POST "https://api.orvion.sh/v1/top-up/user_123" \-H "Content-Type: application/json" \-d '{"amount": "100.00","currency": "USD","description": "Balance top-up"}'
Response Example
{
"charge_id": "txn_abc123",
"checkout_url": "https://pay.orvion.sh/checkout/txn_abc123",
"amount": "100.00",
"currency": "USD",
"customer_ref": "user_123"
}
Get Balance Info (Public)
Check balance information before topping up:
/internal/top-up/{customer_ref}Query Parameters:
currency(optional, default: USD) - Currency codeorganization_id(optional) - Organization ID to use when multiple merchants have the same customer_ref
Example:
# Get balance infocurl -X GET "https://api.orvion.sh/v1/top-up/user_123?currency=USD"
Response:
{
"customer_ref": "user_123",
"currency": "USD",
"available_amount": "75.50",
"organization_name": "Acme Corp"
}
Public Top-Up Flow
1. Consumer Visits Page 2. Check Balance 3. Enter Amount
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ Visit: │ │ GET /top-up/ │ │ User enters amount │
│ /top-up/user_123 │ ──▶ │ {customer_ref} │ ──▶ │ and clicks "Top Up" │
│ │ │ │ │ │
│ Shows current │ │ Returns balance │ │ POST /top-up/ │
│ balance (if exists) │ │ info │ │ {customer_ref} │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
│
▼
4. Create Charge 5. Redirect to Checkout 6. Customer Pays
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ System finds org │ │ Redirect to │ │ Customer connects │
│ from balance │ ──▶ │ checkout_url │ ──▶ │ wallet and pays │
│ │ │ │ │ │
│ Creates charge with │ │ pay.orvion.sh/ │ │ Transaction │
│ metadata: │ │ checkout/txn_abc │ │ confirmed on-chain │
│ purpose: balance_ │ │ │ │ │
│ topup │ │ │ │ │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
│
▼
7. Payment Confirmed 8. Auto Top-Up Balance 9. Redirect Back
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │ │ │
│ Webhook: │ │ Balance topped up │ │ Customer redirected │
│ billing.transaction.│ ──▶ │ automatically │ ──▶ │ to return_url │
│ succeeded │ │ │ │ │
│ │ │ Balance: $175.50 │ │ /top-up/user_123? │
│ Metadata: │ │ │ │ currency=USD │
│ purpose: balance_ │ │ │ │ │
│ topup │ │ │ │ │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
Frontend Integration
You can direct consumers to the hosted top-up page:
https://pay.orvion.sh/top-up/{customer_ref}?currency=USD
Or integrate the API directly into your own UI:
// JavaScript exampleasync function topUpBalance(customerRef, amount, currency = 'USD') {const response = await fetch(`https://api.orvion.sh/v1/top-up/${customerRef}`, {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({amount: amount.toString(),currency,description: 'Balance top-up',}),});if (!response.ok) {const error = await response.json();throw new Error(error.detail || 'Failed to create top-up');}const data = await response.json();// Redirect to checkoutwindow.location.href = data.checkout_url;}// UsagetopUpBalance('user_123', 100.00, 'USD');
Important Notes
- No Authentication Required: These endpoints are public and don't require API keys
- Organization Discovery: The system automatically finds the organization from the customer's existing balance. If multiple merchants have the same
customer_ref, you must provideorganization_idto disambiguate - Multiple Merchants: If multiple merchants match, the API returns a
400 Bad Requesterror listing the available merchants. Includeorganization_idin your request to specify which merchant to use - Automatic Top-Up: When payment succeeds, the balance is automatically topped up via webhook handler
- Balance Must Exist: A balance must be created first (via merchant API) before consumers can top up
- Currency Matching: The currency must match the existing balance currency
List Balances
Get all balances for your organization, optionally filtered by customer.
Endpoint
/v1/metered-billing/balancesQuery Parameters:
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| customer_ref | string | null | Optional | Filter by customer reference | user_123 |
| limit | number | Optional | Maximum number of balances to return (default: 100) | 100 |
| offset | number | Optional | Pagination offset (default: 0) | 0 |
Example: cURL
# List all balancescurl -X GET "https://api.orvion.sh/v1/metered-billing/balances" \-H "Authorization: Bearer $ORVION_API_KEY"# Filter by customercurl -X GET "https://api.orvion.sh/v1/metered-billing/balances?customer_ref=user_123" \-H "Authorization: Bearer $ORVION_API_KEY"
Response Example
[
{
"id": "bal_abc123",
"organization_id": "org_xyz",
"customer_ref": "user_123",
"currency": "USD",
"available_amount": "75.50",
"low_balance_threshold": null,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T11:30:00Z"
},
{
"id": "bal_def456",
"organization_id": "org_xyz",
"customer_ref": "user_456",
"currency": "EUR",
"available_amount": "50.00",
"low_balance_threshold": null,
"created_at": "2025-01-15T09:00:00Z",
"updated_at": "2025-01-15T09:00:00Z"
}
]
View Balance Ledger
View all transactions (top-ups and usage deductions) for a specific balance.
Endpoint
/v1/metered-billing/balances/{balance_id}/ledgerQuery Parameters:
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| limit | number | Optional | Maximum number of entries to return (default: 100) | 100 |
| offset | number | Optional | Pagination offset (default: 0) | 0 |
Example: cURL
curl -X GET "https://api.orvion.sh/v1/metered-billing/balances/bal_abc123/ledger" \-H "Authorization: Bearer $ORVION_API_KEY"
Response Example
{
"entries": [
{
"id": "ledger_001",
"balance_id": "bal_abc123",
"amount": "100.00",
"type": "credit",
"reference_type": "top_up",
"reference_id": null,
"invoice_id": null,
"description": "Monthly prepayment",
"metadata": {
"payment_id": "pay_123",
"source": "stripe"
},
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "ledger_002",
"balance_id": "bal_abc123",
"amount": "-0.025",
"type": "debit",
"reference_type": "usage_tick",
"reference_id": "tick_20250115_100000",
"invoice_id": null,
"description": "Usage tick: 10 seconds",
"metadata": null,
"created_at": "2025-01-15T10:00:10Z"
},
{
"id": "ledger_003",
"balance_id": "bal_abc123",
"amount": "-24.475",
"type": "debit",
"reference_type": "settlement",
"reference_id": "sess_xyz789",
"invoice_id": "inv_abc123",
"description": "Session settlement",
"metadata": null,
"created_at": "2025-01-15T11:30:00Z"
}
],
"total": 3
}
Ledger Entry Types
- credit - Top-up (positive amount)
- debit - Usage tick or settlement (negative amount)
Reference Types
- top_up - Balance top-up
- usage_tick - Usage tick deduction
- settlement - Session settlement
Integration with External Payments
For external payment systems (Stripe, PayPal, etc.), use the manual top-up endpoint after receiving payment confirmation:
Example: Stripe Integration
import stripe
from orvion import OrvionClient
stripe.api_key = "your_stripe_key"
orvion = OrvionClient(api_key="your_orvion_key")
async def handle_stripe_payment_success(payment_intent_id: str, customer_ref: str):
# Get payment details from Stripe
payment_intent = stripe.PaymentIntent.retrieve(payment_intent_id)
# Top up balance in Orvion (manual credit)
balance = await orvion.metered_billing.top_up_balance(
customer_ref=customer_ref,
currency="USD",
amount=str(payment_intent.amount / 100), # Convert cents to dollars
description=f"Stripe payment {payment_intent_id}",
metadata={
"stripe_payment_intent_id": payment_intent_id,
"stripe_customer_id": payment_intent.customer,
"payment_method": "stripe"
}
)
return balance
Note: For Orvion's native payment flow, use top-up-with-payment instead - it handles everything automatically!
Low Balance Alerts
Set a low_balance_threshold to monitor when balances are running low. You can check balances periodically and send alerts:
balances = await orvion.metered_billing.list_balances()
for balance in balances:
if balance.low_balance_threshold:
if float(balance.available_amount) < float(balance.low_balance_threshold):
# Send alert to customer
send_low_balance_alert(balance.customer_ref, balance.available_amount)
Important Notes
Currency Matching
Balances are per-currency. A customer can have separate balances for USD, EUR, etc. Usage sessions must match the balance currency.
Automatic Deduction
When usage ticks are reported, the cost is automatically deducted from the customer's balance. No manual intervention needed.
Negative Balances
Balances can go negative if usage exceeds available funds. You can:
- Allow negative balances and settle later
- Stop sessions when balance is insufficient
- Top up balance to cover usage
Next Steps
- Create Sessions - Start tracking usage
- Report Usage - Track consumption
- Settle Sessions - Generate invoices