Skip to content

Budgets

A distribution can carry a budget. It is optional, and two of the three modes cost you nothing to use.

budget: { mode: "funded", asset: "credit", total: 50000 }
modeReserves moneyBehaviour
noneNoThe default when budget is omitted. Rewards accrue as usual
meteredNoSpend is recorded against the distribution, nothing is held
fundedYesThe total is reserved from the brand wallet at launch

asset is credit or usd. total is in minor units of that asset — 50000 credits, or 50000 = $500.00 in USD.

budget is always present on a distribution, even when the mode is none:

"budget": {
"mode": "funded",
"asset": "credit",
"total": 50000,
"consumed": 12000,
"released": 0
}

consumed and released come off the live reservation and are 0 when no reservation exists yet. total - consumed - released is what remains held.

mode: "funded" requires both an asset and a positive total. Without them, validate() returns budget_incomplete and the distribution stays in draft.

const result = await boomin.distributions.validate(id);
// errors: [{ code: "budget_incomplete", message: "…" }]

Reservation happens at launch, not at create

Section titled “Reservation happens at launch, not at create”

Creating a funded distribution moves no money. The reservation is taken by the launch operation, as its first real step:

  1. Move total from the brand wallet’s available bucket to its reserved bucket.
  2. Open the reservation row and emit budget.reserved.
  3. Proceed to plan and create deployments.

The transfer is keyed on the distribution, so a retried launch can never double-reserve.

If the wallet cannot cover the total, the launch does not fail. It emits budget.reserve_failed and parks:

const operation = await boomin.operations.wait(accepted.operation, { timeout: 120000 });
console.log(operation.status, operation.waitingReason);
// "waiting" "funding_required"

Top the brand wallet up in the app and the operation proceeds — it is woken when the blocker clears, with a cron poller as the backstop.

The SDK also exports a FundingRequiredError for surfaces that raise the same condition synchronously — see Errors.

As rewards are granted against the distribution, the reservation draws down: each grant claims its amount exactly once and moves it from reserved to pending. budget.consumed rises accordingly.

The claim is keyed on the reward grant, so a replayed or retried grant cannot consume twice — the same guarantee the unfunded reward path already has.

The unconsumed remainder returns to the wallet’s available bucket, and budget.released rises, when the distribution is canceled or completed.

await boomin.distributions.cancel(id);
// released = total - consumed

Consumed budget stays consumed — cancellation reclaims what was never spent, not what was.

A budget.released event is emitted on the events feed and delivered to subscribed webhooks.

Each deployment carries budgetAllocationMinor — its share of a funded budget when one has been allocated to it. It is null for unfunded distributions and for deployments with no allocation.

await boomin.webhooks.endpoints.create({
url: "https://your-app.com/webhooks/boomin",
enabledEvents: ["budget.reserved", "budget.released", "budget.reserve_failed"],
});

budget.reserve_failed is the one to alert on: it means a launch is parked waiting for you.

Credit-funded budgets work day one from your existing credit balance. USD budgets draw on a dedicated wallet top-up, done in the app — value only ever enters the ledger at the platform boundary, never through the Platform API.

There is no API call that mints money.