Core concepts
Wallets and holds
Where the money sits, and what a hold actually is.
Every end user has one balance per mode. A balance has a balance and a reserved amount, and what a call may spend is the difference between them.
available = balance − reservedInspect a balance
seams users show ada
# external id ada
# bundle pro
# available $4.97
# reserved $0.00import { Seams } from "@ourseams/sdk"
const seams = new Seams({ apiKey: process.env.SEAMS_API_KEY! })
const user = await seams.users.show("ada")
// user.balance.balanceUnits · user.balance.reservedUnitsimport os
from seams import Seams
seams = Seams(api_key=os.environ["SEAMS_API_KEY"])
user = seams.users.show("ada")
# user.balance.balance_units · user.balance.reserved_unitsCredit the balance
A grant lands immediately on the money wallet and writes a ledger entry. Always micros, the CLI takes dollars; the SDKs take micro-dollars as a string (5000000 is $5.00). It does not mint named plan credits; those come from a bundle include. See Credit systems.
seams users grant 5.00 --user ada --reason "trial top-up"
# granted $5.00 → ada · available $9.97await seams.grants.issue({
endUsers: ["ada"],
amountUnits: "5000000",
reason: "trial top-up",
})seams.grants.issue(
end_users=["ada"],
amount_units="5000000",
reason="trial top-up",
)Why a hold and not a debit
A streaming call's final cost is unknown when it starts. Debiting the worst case up front would show your customer a balance that is wrong for the length of the request; holding it shows them a balance that is honest and an amount that is pending.
| Movement | When | Effect |
|---|---|---|
reserve | Before dispatch | reserved goes up, balance unchanged |
settle | On completion | balance goes down by actual cost, reserved released |
release | On failure | reserved goes down, balance untouched |
Both movements happen in one transaction. A hold released in a second transaction can be released twice, or not at all.