Class: CreditManager
Defined in: javascript/src/manager.ts:222
Orchestrates credit operations.
The deduction path is a single atomic, idempotency-keyed store call
(deductWithAllowance) that consumes free allowance, enforces spend caps,
applies the balance floor and debits the net amount in one transaction
(contract §2). The manager is a thin layer that calculates the cost, maps
the store's typed error codes to exceptions, and emits lifecycle events
only after the operation has succeeded (contract §6).
Optionally accepts a CreditEventEmitter to emit lifecycle events
(deducted, deduct_failed, added, refunded, refund_failed, expired,
cap_reached, cap_warning, low_balance).
Constructors
Constructor
new CreditManager(
store,engine?,emitter?,options?):CreditManager
Defined in: javascript/src/manager.ts:246
Parameters
store
engine?
PricingEngine | null
emitter?
CreditEventEmitter | null
options?
CreditManagerOptions | null
Returns
CreditManager
Accessors
pricingEngine
Get Signature
get pricingEngine():
PricingEngine|null
Defined in: javascript/src/manager.ts:350
The current PricingEngine, or null if not loaded.
Returns
PricingEngine | null
Methods
addCredits()
addCredits(
userId,amount,options?):Promise<AddCreditsResult>
Defined in: javascript/src/manager.ts:441
Add credits to a user's account.
Parameters
userId
string
amount
number | Decimal
options?
expiresAt?
Date | null
idempotencyKey?
string | null
Replay-safe idempotency key (parity with deduct/settle/refund).
metadata?
CreditMetadata | null
tier?
string | null
Target credit tier (credit tiers); omitted resolves to the config's default tier.
type?
string
Returns
Promise<AddCreditsResult>
aggregateStats()
aggregateStats(
start,end):Promise<AggregateStats>
Defined in: javascript/src/manager.ts:1473
Aggregate statistics across all users in a time window.
Parameters
start
Date
end
Date
Returns
Promise<AggregateStats>
canAfford()
canAfford(
userId,metricsOrAmount,options?):Promise<CanAffordResult>
Defined in: javascript/src/manager.ts:971
Advisory affordability check — UI only, non-locking, may be stale (D4/H3).
Never use this as an admission gate; only reserve is authoritative.
Parameters
userId
string
metricsOrAmount
MetricsOrAmount
options?
Returns
Promise<CanAffordResult>
checkAllowance()
checkAllowance(
userId):Promise<AllowanceResult>
Defined in: javascript/src/manager.ts:1025
Get remaining free allowance for the current billing period.
Convenience wrapper that routes through the manager so callers never need to reach past it into the raw store. Returns a zero-allowance result for planless users (no exception).
For rolling_30d/anniversary plans (WS9), resolves and threads
periodStart so the reported window matches the one actually used by
deduct/settle/reserve — a calendar_month plan (the default)
takes the fast path unchanged.
Parameters
userId
string
Returns
Promise<AllowanceResult>
checkFeature()
checkFeature(
userId,feature):Promise<CheckFeatureResult>
Defined in: javascript/src/manager.ts:381
Check whether a user's plan has a specific feature entitlement.
Passthrough to the store, which distinguishes presence from truthiness
(numeric 0 / "" count as present; only null/undefined/false/absent
read as missing — contract §5 / M6).
Parameters
userId
string
feature
string
Returns
Promise<CheckFeatureResult>
checkFeatureLimit()
checkFeatureLimit(
userId,feature):Promise<FeatureLimitResult>
Defined in: javascript/src/manager.ts:395
Advisory, non-locking read of a per-feature invocation-count limit (UI only).
Resolves the FeatureLimit (if any) from the user's plan and the
calendar-aligned window, then delegates counting to the store. Returns
limited: false (zeroed fields, action: null) when no FeatureLimit
is configured for feature on the user's plan — never used for
admission control; that is exclusively the atomic check-and-increment
inside deduct/reserve.
Parameters
userId
string
feature
string
Returns
Promise<FeatureLimitResult>
dailySpend()
dailySpend(
start,end):Promise<DailySpendRow[]>
Defined in: javascript/src/manager.ts:1508
Daily spend aggregation in a time window.
Parameters
start
Date
end
Date
Returns
Promise<DailySpendRow[]>
deduct()
deduct(
userId,metrics,idempotencyKey?,metadata?,feature?):Promise<DeductionResult>
Defined in: javascript/src/manager.ts:1172
Full deduction flow as one atomic store call (contract §2).
breakdown = engine.calculate(metrics);cost = breakdown.total(exactDecimal, no truncation).- If
cost <= 0short-circuit with a zero-amount result. - Otherwise
store.deductWithAllowanceconsumes allowance, enforces caps, applies the balance floor and debits — idempotency-keyed end-to-end.
On a store error a credits.deduct_failed event is emitted and a
typed exception is thrown (insufficient_credits → InsufficientCreditsError,
cap_reached → CapReachedError). No success event is emitted on error.
Parameters
userId
string
metrics
idempotencyKey?
string | null
metadata?
CreditMetadata | null
feature?
string | null
Named feature to enforce/tag a per-feature invocation-count limit for.
Returns
Promise<DeductionResult>
deductFixed()
deductFixed(
userId,jobName,idempotencyKey?,metadata?):Promise<DeductionResult>
Defined in: javascript/src/manager.ts:1424
Shortcut for fixed-cost batch jobs.
L1: an unknown/typo'd jobName is rejected (throws) instead of silently
charging 0 credits — engine.getFixedCost(jobName) === null means the job
is not configured.
Parameters
userId
string
jobName
string
idempotencyKey?
string | null
metadata?
CreditMetadata | null
Returns
Promise<DeductionResult>
deductTeam()
deductTeam(
teamId,userId,metrics,idempotencyKey?,metadata?):Promise<TeamDeductionResult>
Defined in: javascript/src/manager.ts:1363
Deduct from a team's shared balance pool.
Calculates the cost via the pricing engine (exact Decimal, no truncation),
then debits the team balance. Threads an optional idempotencyKey through
to the store so retried team charges are not double-counted (H12).
Parameters
teamId
string
userId
string
metrics
idempotencyKey?
string | null
metadata?
CreditMetadata | null
Returns
Promise<TeamDeductionResult>
getAvailable()
getAvailable(
userId):Promise<AvailableResult>
Defined in: javascript/src/manager.ts:1002
Advisory available = balance − Σ active holds read (UI only, D4/H3).
Parameters
userId
string
Returns
Promise<AvailableResult>
getBalance()
getBalance(
userId):Promise<BalanceResult>
Defined in: javascript/src/manager.ts:435
Get a user's current credit balance.
Parameters
userId
string
Returns
Promise<BalanceResult>
getCreditTiers()
getCreditTiers(
userId):Promise<TierBalancesResult>
Defined in: javascript/src/manager.ts:1008
Get a user's per-tier credit balances (credit tiers). Thin pass-through, no event emission.
Parameters
userId
string
Returns
Promise<TierBalancesResult>
getUserPlan()
getUserPlan(
userId):Promise<GetUserPlanResult>
Defined in: javascript/src/manager.ts:355
Fetch a user's current plan (including feature entitlements).
Parameters
userId
string
Returns
Promise<GetUserPlanResult>
grantSubscriptionCycle()
grantSubscriptionCycle(
userId,amount,options?):Promise<AddCreditsResult>
Defined in: javascript/src/manager.ts:502
Grant one billing-cycle's worth of credits — idempotent-safe for a payment-provider webhook handler (Stripe, etc. — bursar stays provider-agnostic) to call even on webhook redelivery.
- At most one of
expiresAt/ttlDaysmay be given (throwsConfigErrorotherwise). - When
ttlDaysis given,expiresAt = now + ttlDaysdays. - When
replacePrior(defaulttrue), any remaining balance intieris expired immediately via a directstore.addCreditsnegative adjustment — naturally idempotent (a replay finds the tier already at zero and skips the call). - The new cycle is granted via a direct
store.addCreditscall (bypassing addCredits so onlycredits.cycle_renewedfires, not a duplicatecredits.added), threadingidempotencyKeyso a redelivered webhook replays the prior grant instead of double-crediting. - When
planKeyis given, assigns it via setUserPlan. - Emits
credits.cycle_renewedand returns the grant result.
Parameters
userId
string
amount
number | Decimal
options?
GrantSubscriptionCycleOptions
Returns
Promise<AddCreditsResult>
listUsageEvents()
listUsageEvents(
userId,options?):Promise<PaginatedTransactions>
Defined in: javascript/src/manager.ts:1495
Parameters
userId
string
options?
Returns
Promise<PaginatedTransactions>
listUserTransactions()
listUserTransactions(
userId,options?):Promise<PaginatedTransactions>
Defined in: javascript/src/manager.ts:1488
List all user credit transactions with pagination.
Parameters
userId
string
options?
Returns
Promise<PaginatedTransactions>
loadPricingFromStore()
loadPricingFromStore():
Promise<void>
Defined in: javascript/src/manager.ts:309
Load the active pricing config from the store.
Returns
Promise<void>
publishPricing()
publishPricing(
config,label?):Promise<void>
Defined in: javascript/src/manager.ts:334
Publish new pricing and update the engine in one call.
H10: the store write is now awaited (was a fire-and-forget void), so
a persistence failure surfaces to the caller instead of becoming an
unhandled promise rejection.
Parameters
config
label?
string | null
Returns
Promise<void>
publishPricingFromDict()
publishPricingFromDict(
data):Promise<void>
Defined in: javascript/src/manager.ts:302
Load pricing from a PricingConfigData or raw dict and sync it.
Parameters
data
Record<string, unknown> | PricingConfigData
Returns
Promise<void>
refundCredits()
refundCredits(
transactionId,amount?,reason?,metadata?):Promise<RefundResult>
Defined in: javascript/src/manager.ts:1328
Refund a previous credit deduction.
H3: the store's error is checked before emitting. A successful refund
emits credits.refunded; a failed/duplicate/over-refund emits
credits.refund_failed and throws a typed RefundError (no success
event is ever emitted for a failed refund).
Parameters
transactionId
string
amount?
number | Decimal
reason?
string
metadata?
CreditMetadata | null
Returns
Promise<RefundResult>
release()
release(
userId,leaseId):Promise<ReleaseResult>
Defined in: javascript/src/manager.ts:942
Release a lease without charging (work failed/aborted) — idempotent (H1).
Parameters
userId
string
leaseId
string
Returns
Promise<ReleaseResult>
renew()
renew(
userId,leaseId,ttl?):Promise<LeaseResult>
Defined in: javascript/src/manager.ts:954
Extend a lease's TTL for long batch/agentic jobs (B4).
Parameters
userId
string
leaseId
string
ttl?
number | null
Returns
Promise<LeaseResult>
reserve()
reserve(
userId,metricsOrAmount,options?):Promise<LeaseResult>
Defined in: javascript/src/manager.ts:755
Atomically acquire a lease — the only admission control (D4).
Resolves the effective policy, enforces requiredFeature, sizes the hold
from metricsOrAmount (worst-case in strict, estimate in overdraft — the
caller chooses what to pass), and calls the store's atomic createLease. On
any business failure throws the coherent typed exception; on success emits
credits.reserved and returns the LeaseResult.
Parameters
userId
string
metricsOrAmount
MetricsOrAmount
options?
Returns
Promise<LeaseResult>
runBilled()
runBilled<
T>(userId,options):Promise<{deduction:DeductionResult;result:T; }>
Defined in: javascript/src/manager.ts:1056
One-call shortcut wiring reserve → doWork → settle (interface plan §4).
doWork runs the operation and returns { result, actual } where
actual is the real usage metrics (or amount) to settle. On any exception
from doWork the lease is released and the error re-raised. For long jobs
doWork may call renew. A crash between reserve and settle is
covered by the lease TTL (and the store's reaper).
Type Parameters
T
T
Parameters
userId
string
options
Returns
Promise<{ deduction: DeductionResult; result: T; }>
settle()
settle(
userId,leaseId,metricsOrAmount,options?):Promise<DeductionResult>
Defined in: javascript/src/manager.ts:828
Charge the ACTUAL cost against a lease and finalize it (D5).
De-clamped: bills the full actual cost even if it exceeds the lease hold
(overdraft). Never blocks on floor/cap at settle — a cap breach surfaces as a
non-blocking credits.cap_warning/credits.cap_reached signal. Emits
credits.deducted, then multi-level credits.low_balance and a
credits.overdraft signal if the balance went negative.
Parameters
userId
string
leaseId
string
metricsOrAmount
MetricsOrAmount
options?
Returns
Promise<DeductionResult>
setup()
setup():
Promise<SetupResult>
Defined in: javascript/src/manager.ts:297
Run bundled SQL migrations through the store.
Returns
Promise<SetupResult>
setUserPlan()
setUserPlan(
userId,planKey):Promise<void>
Defined in: javascript/src/manager.ts:365
Set a user's subscription plan and emit credits.plan_changed.
The store call is awaited so a persistence failure surfaces to the caller. The event is emitted only after the store write succeeds (contract §6).
Parameters
userId
string
planKey
string
Returns
Promise<void>
spendByModel()
spendByModel(
start,end):Promise<SpendByModelRow[]>
Defined in: javascript/src/manager.ts:1483
Aggregate spend by model in a time window.
Parameters
start
Date
end
Date
Returns
Promise<SpendByModelRow[]>
spendByUser()
spendByUser(
start,end):Promise<SpendByUserRow[]>
Defined in: javascript/src/manager.ts:1478
Aggregate spend by user in a time window.
Parameters
start
Date
end
Date
Returns
Promise<SpendByUserRow[]>
sweepExpiredCredits()
sweepExpiredCredits(
dryRun?):Promise<SweepResult>
Defined in: javascript/src/manager.ts:1466
Sweep expired credits from all users' balances.
When dryRun is true, reports what would be expired without modifying
any balances. Unaffected by options.lazyExpiry — this always runs a
global sweep; lazy expiry only scopes automatic per-user sweeps.
Parameters
dryRun?
boolean = false
Returns
Promise<SweepResult>
topUsers()
topUsers(
limit,start,end):Promise<TopUserRow[]>
Defined in: javascript/src/manager.ts:1503
Top users by spend in a time window with limit.
Parameters
limit
number
start
Date
end
Date
Returns
Promise<TopUserRow[]>