Astrodyne

Billing API

Your balance, every movement of money on your account, and your funding history — derived from an append-only record of every movement.

Endpoints

MethodPathSummary
GET/v1/billing/balanceLedger-derived balance and lifetime totals.
GET/v1/billing/transactionsAppend-only ledger history.
GET/v1/billing/invoicesMonthly usage statements available on this account.
GET/v1/billing/invoices/{number}One monthly usage statement.
GET/v1/billing/fundingFunding-purchase history.

Account-session authentication, like the usage endpoints.

Money is integers
Every amount appears twice: *_units as an integer, and *_usd as a decimal string. 1 USD = 100,000,000 units. Compare and accumulate the integers; display the string. No float ever touches a balance, here or inside Astrodyne.

Balance

balance_units is derived by summing every entry, not read from a counter that could drift. The lifetime totals satisfy funded − spend − refunds + adjustments = balance.

curl
# Balance, derived from the append-only transaction record.
curl https://api.astrodyne.ai/v1/billing/balance \
  -H "Authorization: Bearer $ASTRODYNE_SESSION_TOKEN"

# Transaction history.
curl "https://api.astrodyne.ai/v1/billing/transactions?limit=25" \
  -H "Authorization: Bearer $ASTRODYNE_SESSION_TOKEN"
Python
import os
import httpx

session = {"Authorization": f"Bearer {os.environ['ASTRODYNE_SESSION_TOKEN']}"}

with httpx.Client(base_url="https://api.astrodyne.ai") as http:
    balance = http.get("/v1/billing/balance", headers=session).json()

    # Integer units are the source of truth: 1 USD = 100_000_000 units.
    # The decimal string is provided so you never have to divide.
    print(balance["balance_units"], "units =", balance["balance_usd"], "USD")

    history = http.get("/v1/billing/transactions", headers=session,
                       params={"limit": 25}).json()
    for entry in history["data"]:
        # type is a closed set: funding, usage, refund, reversal, adjustment.
        print(entry["created_at"], entry["type"], entry["amount_usd"])
JavaScript
const session = { Authorization: `Bearer ${process.env.ASTRODYNE_SESSION_TOKEN}` };

const balance = await (await fetch(
  "https://api.astrodyne.ai/v1/billing/balance", { headers: session },
)).json();

// Integer units are the source of truth: 1 USD = 100_000_000 units.
console.log(balance.balance_units, "units =", balance.balance_usd, "USD");

const history = await (await fetch(
  "https://api.astrodyne.ai/v1/billing/transactions?limit=25", { headers: session },
)).json();
for (const entry of history.data) {
  // type is a closed set: funding, usage, refund, reversal, adjustment.
  console.log(entry.created_at, entry.type, entry.amount_usd);
}

Transaction history

Entries are append-only: nothing is ever edited or deleted. A correction appears as a new entry, not as a change to an old one.

type is a closed set:

typeMeaning
fundingMoney you added.
usageA settled API charge. reference_id is the request id.
refundMoney returned to you.
reversalA reversed movement.
adjustmentA manual correction.

An internal category that does not map to one of these appears as adjustment rather than leaking a new name, so the set above is the complete list a client needs to handle.

Funding history

Purchases carry an opaque id, timestamps, status, amounts, and payment_method. Payment-processor identifiers are deliberately absent — a payment-intent id is a handle to an object in someone else's system, and publishing it would be handing out a reference you cannot revoke.

A redelivered payment webhook does not duplicate a purchase: the purchase is the unit, not the event.

Checking the numbers