Preview. Usage Billing v1 is in development, and demos run on sample data.Read the guide

Product guide

Overview

Waffo Usage Billing is an API for AI companies that sell to other businesses. You create a customer, sign a contract with its prices, add seats, and call the API before and after each model run. We keep the balances, enforce the limits, top up when funds run low, and produce a statement every month.

It runs on the Waffo metering core, which holds the prepaid money. This layer adds the commercial model on top: customers, contracts, seats, budgets and statements.

Resources

Customer
A team or company you sell to.
Rate card
Your list prices, by product.
Contract
One customer's deal: a rate card, overrides, subscriptions, commits and thresholds. A customer has one active contract.
Override
A discount multiplier or a fixed price on a contract.
Seat
A member of the customer's team, on a tier that sets their monthly included credit.
Team pool
Credit shared by everyone on the contract, with an optional monthly quota.
Commit
Prepaid balance the customer has paid for.
Credit
Free balance, such as promotional or included credit.
Budget
A monthly spending limit on a customer, a pool or a member.
Hold
An amount reserved by an authorize call until usage is reported.
Statement
The monthly record of what a customer owes. It is read only once finalized.

Life of a request

  1. Authorize. Call POST /v1/authorize with an estimate before the model runs. An allowed request gets a hold_id. A denied request gets a reason.
  2. Run. Do the work. Concurrent requests see the held amount, so they cannot spend it again.
  3. Report. Call POST /v1/ingest with the actual quantity and the hold_id. We rate it, swap the hold for the actual cost, and return any difference.
  4. Check top-up. If the balance fell below your threshold, a top-up opens.

Reported usage is never refused, because the work already happened. If the actual cost is above what the customer has left, the balance goes negative and the next top-up covers it first.

Drawdown order

Every request draws in the same order: seat credit, pool monthly credit, prepaid balance, then overage if the contract allows it. Overage is off by default.

Member limits, pool quotas and customer budgets count spend after seat credit. A member who has used up their limit can still spend their own included credit.

Budgets and quotas

A budget can be hard, which denies requests at the limit, or soft, which sends an alert and keeps serving. Changes apply at once. A budget set below current spend blocks the next request.

Top-ups do not count against a budget. Budgets count rated spend at contract prices.

Auto top-up

Set a threshold and either an amount to add or a balance to refill to. When the spendable balance falls below the threshold, one top-up opens. While it is open, no second one can start, however many requests cross the line.

Frequency limits cap how many top-ups can run in a window. The rule is also checked on a timer, so a customer who is already blocked still gets refilled when the window opens.

You choose who takes the payment. In external mode your system charges the customer and reports the result.

Statements

Each period closes into a statement with seat fees, prorations, overage and credit notes. Finalized statements never change. Corrections are credit notes on a later statement, and usage that arrives late is carried forward to the next one.

We produce the statement. Your payment provider handles collection, tax and invoice delivery.

Money format

Amounts in JSON are decimal strings such as "12.500000000", or integers in minor units such as amount_cents: 1999. A JSON number with a fraction in a money field returns 400 validation_failed. Each usage event is rated once, with banker's rounding.

Idempotency

Every write takes an Idempotency-Key header. Keys are kept for 48 hours. The same key with the same body returns the stored response. The same key with a different body returns 409 idempotency_conflict.

Usage events dedupe on transaction_id for 34 days. Authorize dedupes on request_id per customer.

Errors

Errors share one shape, with a stable code you can branch on:

{
  "error": {
    "code": "idempotency_conflict",
    "message": "This Idempotency-Key was used with a different request body.",
    "retryable": false,
    "request_id": "req_01J9..."
  }
}

A denied authorize is not an error. It returns 200 with allowed: false and a reason. The API reference lists every code for every endpoint, with its status, retry answer, trigger and side effects.

What v1 leaves out

Self-serve checkout, tax calculation, currency conversion, ERP and CRM sync, marketplace billing and full dunning are outside v1. The first release ships the API and portal tokens. A hosted portal UI comes later.