BetterIAM
Server API

billing

Billing tells an organization what it spends, on what, and who spent it: usage recorded on meters is priced from a rate card per billing account and month, the…

Billing tells an organization what it spends, on what, and who spent it: usage recorded on meters is priced from a rate card per billing account and month, then shared out to the people, agents, teams, departments and projects that produced it. Budgets alert and can refuse usage, and the platform issues monthly invoices (statements) with credit applied. Invoicing works like Stripe or Orb: drafts, finalized, paid, uncollectible and void invoices; one-off invoice items; payments and credit notes; plans with fees, seats and meter prices; subscriptions with trials and prorations; coupons; payment reminders and a printable invoice. Server code records usage, takes payments and runs the jobs through iam.billing (no credential). The repository guide is docs/billing.md.

How spend is computed

A billing account is an organization, or a tenant below it with a billing profile of its own. For each account and month, every meter's total is priced once (tiers and free units apply to the account's total), and the cost is shared out to the daily roll-ups of usage by quantity; for unique meters every person or agent costs the same. Reported meters carry their own cost per event. Person, team, department and project spend therefore add up to the account's charges. Meters an account defines for itself are chargeback only (internal) and never appear on a statement.

Usage is attributed when it is recorded: the identity, its direct teams (split evenly by default, see the billing option teamAttribution), and its department (or its first team's). An agent's usage counts toward its sponsor's teams and department. Money is in micros of the deployment currency (costMicros), with rounded amount fields in currency units.

Permissions

iam:billing:read reads spend, budgets, credits, profiles, invoices, plans, subscriptions and discounts; iam:billing:manage defines meters and prices in the tenant, budgets and profiles, and lets an account's billing managers subscribe to self-serve plans and redeem coupon codes; iam:billing:record records usage. Resources are iam/billing, iam/billing/meters/{key}, iam/billing/budgets, iam/billing/profile, iam/billing/credits, iam/billing/statements, iam/billing/invoice-items, iam/billing/plans, iam/billing/subscriptions, iam/billing/coupons and iam/billing/discounts. Credits, closing a month, invoice items, payments, credit notes, plans and coupons are for root administrators only; they act on any account's invoice by calling on the root tenant.

Methods62
Serveriam.api.billing
Clientclient.billing
HTTPPOST /api/iam/billing/*
MethodWhat it doesAccess
accountsLists the billing accounts in the tenant's subtree with this month's billable spend, projection, available credit and latest statement.Credential
anomaliesLists the people, teams and meters whose spend on one day jumped far above their usual daily spend.Credential
cancelSubscriptionCancels a subscription at the end of the month (default) or now.Credential
changePlanMoves a subscription to another plan now, keeping its seats and what is left of its trial.Credential
checkReports whether the caller's own usage is within every enforced budget that covers it.Credential
closePeriodIssues statements for a month that has ended, for every billing account or only accountId's.Credential
createBudgetCreates a spend budget for the tenant, a tenant below it, a team, a department or a person, per month, quarter or year.Credential
createCouponCreates a coupon accounts can redeem by code.Credential
createCreditNoteIssues a credit note against a finalized, paid or uncollectible invoice.Credential
createInvoiceItemAdds a one-off charge, or with a negative amount a credit, to the billing account's next invoice.Credential
createMeterDefines a usage meter: a platform meter on the root tenant, a chargeback meter for the tenant's subtree elsewhere.Credential
createPlanDefines a plan on the platform (root) tenant.Credential
deactivateCouponStops a coupon (by code) from being redeemed; accounts that redeemed it keep their discount.Credential
deleteBudgetDeletes a budget and its alert history.Credential
deleteInvoiceItemDeletes a pending invoice item.Credential
deleteMeterDeletes a meter that has never recorded usage, with its prices.Credential
deleteProfileRemoves a billing profile, so the tenant's usage rolls into its parent's account again.Credential
departmentSpendReports a department's spend, with the departments below it, grouped by identity by default.Credential
exportSpendReturns a spend report as a CSV file: one row per group with the amount, share, events and a column per meter's quantity.Credential
exportStatementReturns a statement as a CSV file: its lines, credit and total, then the breakdown by project, team, department and person.Credential
finalizeInvoiceFinalizes a draft invoice: recomputes it with the latest usage and invoice items, numbers it, seals it and emails it.Credential
getProfileReturns the billing profile of the tenant (or of targetTenantId below it) and the account that pays for it.Credential
getStatementReturns one statement with its lines, credit, breakdown and bill-to details, re-checking its content hash.Credential
getTermsReturns the contract terms of the tenant's billing account: discount, minimum monthly commitment and tax.Credential
grantCreditGrants credit to a billing account, which its statements draw on, earliest expiry first.Credential
listBudgetsLists the tenant's budgets with their spend, projection and the thresholds reached in the current window.Credential
listCouponsLists the platform's coupons, newest first, with their redemptions.Credential
listCreditNotesLists the credit notes of the billing accounts in the tenant's subtree, or of one statementId, newest first.Credential
listCreditsLists the credit of the tenant's billing account with the available balance.Credential
listDiscountsLists the discounts (redeemed coupons) of the billing accounts in the tenant's subtree.Credential
listInvoiceItemsLists invoice items of the billing accounts in the tenant's subtree, newest first, optionally by status.Credential
listMetersLists the meters that reach the tenant with the price that applies to its billing account this month.Credential
listPlansLists the platform's plans with their fees, seats and meter prices.Credential
listPricesReturns a meter's rate card as the tenant may see it, and the entry that prices its account this month.Credential
listStatementsLists the statements of the billing accounts in the tenant's subtree (all of them for the root), newest month first.Credential
listSubscriptionsLists the subscriptions of the billing accounts in the tenant's subtree, newest first.Credential
listUsageLists raw usage events recorded in the tenant for a month, newest first.Credential
markPaidMarks a finalized or uncollectible statement paid by recording a manual payment of the amount due, with an optional reference.Credential
markUncollectibleWrites a finalized invoice off as uncollectible; a later payment still settles it.Credential
mySpendReports the caller's own spend: their usage and that of the agents they sponsor, with the budgets set on them.Credential
previewStatementBuilds the statement a billing account would receive for a month (the current one so far by default), without issuing it.Credential
quotePrices a quantity of a meter as a month total for the tenant's billing account.Credential
recordRecords usage of a meter in the tenant, attributed to an identity, their teams and department, with optional tags.Credential
recordManyRecords up to 100 usage events in one transaction, all or nothing.Credential
recordPaymentRecords a payment against a finalized or uncollectible invoice.Credential
redeemCouponRedeems a coupon code for the billing account.Credential
removeDiscountEnds a billing account's discount now.Credential
renderInvoiceReturns an invoice as a standalone HTML page to print or save as PDF.Credential
resumeSubscriptionUndoes a cancellation at the end of the month before it takes effect.Credential
revokeCreditWithdraws what is left of a credit.Credential
setPriceSets or removes a rate-card price for a meter the tenant defines: its list price, or a negotiated price for a tenant below it.Credential
setProfileCreates or updates a billing profile: company, billing emails, tax ID, address, purchase order, cost center, payment terms.Credential
setTermsSets a billing account's contract terms: a discount off the subtotal, a minimum monthly commitment, and the tax invoices add.Credential
spendReports the spend of the tenant and every tenant below it for a month, grouped and filtered.Credential
subscribeSubscribes the billing account to a plan (id or key).Credential
teamSpendReports a team's spend, with the teams below it, grouped by identity by default.Credential
trendReturns monthly totals for the last months months (1 to 24, default 6), with the filters of spend.Credential
updateBudgetChanges a budget's name, amount, period, meters, thresholds, alerts or enforcement; its subject stays.Credential
updateMeterRenames a meter, changes its unit or description, or archives it.Credential
updatePlanChanges a plan, by id or key: name, items, description, trial, self-serve, archived.Credential
updateSubscriptionChanges a subscription's seats.Credential
voidStatementVoids a finalized statement: its credit, invoice items, coupons and advance-billed months come back and the month reopens.Credential

accounts

Lists the billing accounts in the tenant's subtree with this month's billable spend, projection, available credit and latest statement.

POST/api/iam/billing/accounts
client.billing.accounts()Credential
  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.

On the root tenant this is every organization: the platform's receivables view. monthToDateMicros counts only meters defined above the account (what a statement would bill); totalMicros includes the account's own chargeback meters.

Input

Prop

Type

Returns

An array of AccountOverview.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/accounts" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.accounts(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<AccountOverview[]>

anomalies

Lists the people, teams and meters whose spend on one day jumped far above their usual daily spend.

POST/api/iam/billing/anomalies
client.billing.anomalies()Credential
  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.
  • Errors: INVALID_INPUT for a malformed day, baselineDays outside 3 to 90, factor outside 1.1 to 1000, or a negative minimum.

day defaults to yesterday in the billing time zone. A spike is spend at least factor (3) times the average over the baselineDays (14) before it and at least minimum (10 currency units) more; spending with no baseline counts when it reaches minimum (factor: null). The largest increases come first, at most 50. The daily job iam.billing.detectAnomalies() alerts on them once each (billing:anomaly, spend-anomaly email).

const { anomalies } = await iam.api.billing.anomalies(admin, { tenantId, factor: 5 });
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/anomalies" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.anomalies(
  credential: CredentialInput,
  input: { tenantId: string } & AnomalyOptions,
): Promise<{ tenantId: string; anomalies: SpendAnomaly[] }>

cancelSubscription

Cancels a subscription at the end of the month (default) or now.

POST/api/iam/billing/cancelSubscription
client.billing.cancelSubscription()Credential
  • Permission: iam:billing:manage on iam/billing/subscriptions (self-serve plans, at the month's end only), or a root administrator.
  • Audited as: billing:subscription-cancel.
  • Errors: ACCESS_DENIED (403) for an immediate cancellation by anyone but a root administrator; INVALID_TRANSITION (409) for an ended subscription.

At the month's end the subscription keeps running and resumeSubscription can undo it. With atPeriodEnd: false it ends now and the unused part of this month's advance fees and seats is credited as invoice items; arrears items bill the part of the month it ran.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/cancelSubscription" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "subscriptionId": "<subscriptionId>"
}'
Signature
iam.api.billing.cancelSubscription(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string; atPeriodEnd?: boolean },
): Promise<{ subscription: SubscriptionView; invoiceItems: InvoiceItemView[] }>

changePlan

Moves a subscription to another plan now, keeping its seats and what is left of its trial.

POST/api/iam/billing/changePlan
client.billing.changePlan()Credential
  • Permission: iam:billing:manage on iam/billing/subscriptions when both plans are self-serve, or a root administrator.
  • Audited as: billing:subscription-plan-change.
  • Errors: CONFLICT (409) when the account already subscribes to the new plan; INVALID_TRANSITION (409) for an ended subscription or an archived plan; INVALID_INPUT for the same plan.

The old subscription ends and a new one starts. The old plan's unused advance charges are credited and the new plan's charges for the rest of the month added, both as invoice items for the next invoice (invoiceItems).

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/changePlan" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "subscriptionId": "<subscriptionId>",
  "plan": "<plan>"
}'
Signature
iam.api.billing.changePlan(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string; plan: string },
): Promise<{ subscription: SubscriptionView; invoiceItems: InvoiceItemView[] }>

check

Reports whether the caller's own usage is within every enforced budget that covers it.

POST/api/iam/billing/check
client.billing.check()Credential
  • Permission: The caller's own session; the tenant must be the caller's tenant or one below it.
  • Audited as: not audited.
  • Errors: ACCESS_DENIED (403) for a tenant outside the caller's.

Covering budgets are enforced tenant budgets on the tenant or an ancestor, and the caller's own, their teams' (with parent teams) and their department's (with the departments above it); meter narrows to budgets that count it. blockedBy names the first spent budget. Statuses may lag recorded usage by up to 30 seconds. Server code checks any identity with iam.billing.check.

const verdict = await client.billing.check({ tenantId, meter: 'api-calls' });
if (!verdict.allowed) showBudgetBanner(verdict.blockedBy);
Input

Prop

Type

Returns

A SpendCheck object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/check" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.check(
  credential: CredentialInput,
  input: { tenantId: string; meter?: string },
): Promise<SpendCheck>

closePeriod

Issues statements for a month that has ended, for every billing account or only accountId's.

POST/api/iam/billing/closePeriod
client.billing.closePeriod()Credential
  • Permission: Root administrators only, called on the root tenant (iam:billing:manage on iam/billing/periods).
  • Audited as: iam:billing:manage; each statement as billing:statement.
  • Errors: INVALID_INPUT for the current or a future month, or when called on another tenant.

period defaults to last month. Each invoice bills the month's usage of platform meters, the subscriptions' fees and seats (the month itself for arrears items, the next month for advance ones), and pending invoice items. Accounts already invoiced for the month and accounts with nothing to bill are skipped (skipped.existing, skipped.empty), so the job is safe to repeat. Coupons apply after the contract discount, credit earliest expiry first; invoices are emailed (billing-statement) to the profile's billing emails or the owners, and raw usage events past their retention are deleted (sweptUsage). With draft: true (or the option billing.autoFinalize: false) invoices are kept as drafts, refreshed on every run, and listed in drafted; finalize them with finalizeInvoice. Schedulers call iam.billing.closePeriod() instead.

Input

Prop

Type

Returns

A ClosePeriodResult object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/closePeriod" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.closePeriod(
  credential: CredentialInput,
  input: { tenantId: string; period?: string; accountId?: string; draft?: boolean },
): Promise<ClosePeriodResult>

createBudget

Creates a spend budget for the tenant, a tenant below it, a team, a department or a person, per month, quarter or year.

POST/api/iam/billing/createBudget
client.billing.createBudget()Credential

Used inBilling and spend

  • Permission: iam:billing:manage on iam/billing/budgets.
  • Audited as: billing:budget-create.
  • Errors: CONFLICT (409) for a name already used (case-insensitive); LIMIT_EXCEEDED (409) past 200 budgets; NOT_FOUND for a subject outside the tenant; INVALID_INPUT for an amount of 0, a malformed meter key, more than 10 thresholds or notification addresses.

amount is in currency units. thresholds (default 50, 80, 100 percent) and forecastAlerts (default on) drive the alerts that iam.billing.checkBudgets() sends once per window to the owners, the subject (the person, the team's maintainers or the department head) and notify.emails. enforce makes covered usage fail once the budget is spent. The result is the budget with its current standing.

await iam.api.billing.createBudget(admin, {
  tenantId,
  name: 'Platform team monthly',
  subjectType: 'team',
  subjectId: platformTeamId,
  amount: 600,
  notify: { emails: ['finance@acme.test'] },
});
Input

Prop

Type

Returns

A BillingBudgetView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/createBudget" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "name": "<name>",
  "amount": 1
}'
Signature
iam.api.billing.createBudget(
  credential: CredentialInput,
  input: { tenantId: string } & BillingBudgetInput,
): Promise<BillingBudgetView>

createCoupon

Creates a coupon accounts can redeem by code.

POST/api/iam/billing/createCoupon
client.billing.createCoupon()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/coupons), called on the root tenant.
  • Audited as: billing:coupon.
  • Errors: CONFLICT (409) for a code already used; INVALID_INPUT for a code that is not 3 to 32 letters, digits, - or _, both or neither of percentOff and amountOff, a percentage outside 0 to 100, durationInMonths without duration: 'repeating', or a redeemBy in the past.

Codes are stored in upper case. duration is once (default: the next invoice), repeating (invoices for durationInMonths months from the month of redemption) or forever. maxRedemptions caps how many accounts may redeem it.

await iam.api.billing.createCoupon(root, {
  tenantId: rootTenantId,
  code: 'LAUNCH20',
  percentOff: 20,
  duration: 'repeating',
  durationInMonths: 3,
});
Input

Prop

Type

Returns

A CouponView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/createCoupon" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "code": "<code>"
}'
Signature
iam.api.billing.createCoupon(
  credential: CredentialInput,
  input: {
    tenantId: string;
    code: string;
    name?: string;
    percentOff?: number;
    amountOff?: number;
    duration?: BillingCoupon['duration'];
    durationInMonths?: number;
    maxRedemptions?: number;
    redeemBy?: number;
  },
): Promise<CouponView>

createCreditNote

Issues a credit note against a finalized, paid or uncollectible invoice.

POST/api/iam/billing/createCreditNote
client.billing.createCreditNote()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/statements).
  • Audited as: billing:credit-note.
  • Errors: INVALID_INPUT for an amount above what is left to credit, an unknown reason, or a non-boolean refund; INVALID_TRANSITION (409) for a draft or void invoice.

amount defaults to everything not yet credited. The note first reduces the amount due; the rest (a part already paid) becomes account credit, or with refund: true is recorded as refunded outside Better IAM. applied shows the split. Notes are numbered {invoice}-CN-01, -CN-02, ...; reason is duplicate, fraudulent, order_change, product_unsatisfactory or other (default), with an optional memo. An invoice the notes and payments cover is paid.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/createCreditNote" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.createCreditNote(
  credential: CredentialInput,
  input: {
    tenantId: string;
    statementId: string;
    amount?: number;
    reason?: BillingCreditNote['reason'];
    memo?: string;
    refund?: boolean;
  },
): Promise<{ statement: StatementSummary; creditNote: CreditNoteView }>

createInvoiceItem

Adds a one-off charge, or with a negative amount a credit, to the billing account's next invoice.

POST/api/iam/billing/createInvoiceItem
client.billing.createInvoiceItem()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/invoice-items), called on the account.
  • Audited as: billing:invoice-item.
  • Errors: INVALID_INPUT when the tenant is not a billing account, for an empty description, an amount beyond one billion, a quantity of 0, or more than 20 metadata entries; BILLING_PERIOD_CLOSED (409) for a period already invoiced.

amount is per unit in currency units and quantity defaults to 1; the item's total is rounded to the cent. period bills it on the invoice for that month instead of the next one. When credit items exceed an invoice's charges the invoice totals 0 and the rest becomes account credit.

await iam.api.billing.createInvoiceItem(root, {
  tenantId: acmeId,
  description: 'Onboarding workshop',
  amount: 500,
});
Input

Prop

Type

Returns

A InvoiceItemView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/createInvoiceItem" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "description": "<description>",
  "amount": 1
}'
Signature
iam.api.billing.createInvoiceItem(
  credential: CredentialInput,
  input: {
    tenantId: string;
    description: string;
    amount: number;
    quantity?: number;
    period?: string;
    metadata?: Record<string, string>;
  },
): Promise<InvoiceItemView>

createMeter

Defines a usage meter: a platform meter on the root tenant, a chargeback meter for the tenant's subtree elsewhere.

POST/api/iam/billing/createMeter
client.billing.createMeter()Credential

Used inBilling and spend

  • Permission: iam:billing:manage on iam/billing/meters/{key}.
  • Audited as: billing:meter-create.
  • Errors: CONFLICT (409) when a meter with the key already reaches the tenant; LIMIT_EXCEEDED (409) past 100 meters; INVALID_INPUT for a malformed key or a unique reported meter.

Keys are 1 to 64 lowercase letters, digits, dots, underscores or hyphens, starting with a letter. aggregation is sum (default) or unique (distinct people and agents per month); pricing is rate-card (default) or reported (each event carries its cost). Neither can change later.

Input

Prop

Type

Returns

A MeterView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/createMeter" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "key": "<key>",
  "name": "<name>"
}'
Signature
iam.api.billing.createMeter(
  credential: CredentialInput,
  input: {
    tenantId: string;
    key: string;
    name: string;
    unit?: string;
    description?: string;
    aggregation?: BillingMeter['aggregation'];
    pricing?: BillingMeter['pricing'];
  },
): Promise<MeterView>

createPlan

Defines a plan on the platform (root) tenant.

POST/api/iam/billing/createPlan
client.billing.createPlan()Credential

Used inBilling and spend

  • Permission: Root administrators only (iam:billing:manage on iam/billing/plans), called on the root tenant.
  • Audited as: billing:plan.
  • Errors: CONFLICT (409) for a key already used; LIMIT_EXCEEDED (409) past 100 plans; INVALID_INPUT for a malformed key, 0 or more than 20 items, duplicate item ids, two items pricing one meter, or a malformed price.

items are fee (amount per month), seat (unitAmount per seat and month, includedSeats), both billed in advance (default) or arrears, and usage (meter with a price that replaces the rate card for subscribers). trialDays (1 to 365) starts subscriptions with a free trial; selfServe lets account billing managers subscribe.

await iam.api.billing.createPlan(root, {
  tenantId: rootTenantId,
  key: 'team',
  name: 'Team',
  selfServe: true,
  items: [
    { id: 'platform', kind: 'fee', name: 'Platform fee', amount: 99 },
    { id: 'seats', kind: 'seat', name: 'Seats', unitAmount: 12, includedSeats: 3 },
  ],
});
Input

Prop

Type

Returns

A PlanView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/createPlan" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "key": "<key>",
  "name": "<name>",
  "items": [
    {}
  ]
}'
Signature
iam.api.billing.createPlan(
  credential: CredentialInput,
  input: {
    tenantId: string;
    key: string;
    name: string;
    items: unknown[];
    description?: string;
    trialDays?: number;
    selfServe?: boolean;
  },
): Promise<PlanView>

deactivateCoupon

Stops a coupon (by code) from being redeemed; accounts that redeemed it keep their discount.

POST/api/iam/billing/deactivateCoupon
client.billing.deactivateCoupon()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/coupons), called on the root tenant.
  • Audited as: billing:coupon-deactivate.
  • Errors: NOT_FOUND for an unknown code.
Input

Prop

Type

Returns

A CouponView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/deactivateCoupon" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "code": "<code>"
}'
Signature
iam.api.billing.deactivateCoupon(
  credential: CredentialInput,
  input: { tenantId: string; code: string },
): Promise<CouponView>

deleteBudget

Deletes a budget and its alert history.

POST/api/iam/billing/deleteBudget
client.billing.deleteBudget()Credential
  • Permission: iam:billing:manage on iam/billing/budgets.
  • Audited as: billing:budget-delete.
  • Errors: NOT_FOUND for a budget of another tenant.
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/deleteBudget" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "budgetId": "<budgetId>"
}'
Signature
iam.api.billing.deleteBudget(
  credential: CredentialInput,
  input: { tenantId: string; budgetId: string },
): Promise<{ success: true }>

deleteInvoiceItem

Deletes a pending invoice item.

POST/api/iam/billing/deleteInvoiceItem
client.billing.deleteInvoiceItem()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/invoice-items), called on the account.
  • Audited as: billing:invoice-item-delete.
  • Errors: INVALID_TRANSITION (409) for an item already on a finalized invoice (issue a credit note instead).
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/deleteInvoiceItem" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "itemId": "<itemId>"
}'
Signature
iam.api.billing.deleteInvoiceItem(
  credential: CredentialInput,
  input: { tenantId: string; itemId: string },
): Promise<{ success: true }>

deleteMeter

Deletes a meter that has never recorded usage, with its prices.

POST/api/iam/billing/deleteMeter
client.billing.deleteMeter()Credential
  • Permission: iam:billing:manage on iam/billing/meters/{key}.
  • Audited as: billing:meter-delete.
  • Errors: RESOURCE_IN_USE (409) once the meter has recorded usage (archive it with updateMeter instead); NOT_FOUND when the tenant does not define the key.
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/deleteMeter" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "key": "<key>"
}'
Signature
iam.api.billing.deleteMeter(
  credential: CredentialInput,
  input: { tenantId: string; key: string },
): Promise<{ success: true; removedPrices: number }>

deleteProfile

Removes a billing profile, so the tenant's usage rolls into its parent's account again.

POST/api/iam/billing/deleteProfile
client.billing.deleteProfile()Credential
  • Permission: iam:billing:manage on iam/billing/profile; below an organization, called from an ancestor with targetTenantId.
  • Audited as: billing:profile-delete.
  • Errors: NOT_FOUND without a profile; ACCESS_DENIED (403) when a tenant below an organization removes its own.
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/deleteProfile" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.deleteProfile(
  credential: CredentialInput,
  input: { tenantId: string; targetTenantId?: string },
): Promise<{ success: true }>

departmentSpend

Reports a department's spend, with the departments below it, grouped by identity by default.

POST/api/iam/billing/departmentSpend
client.billing.departmentSpend()Credential
  • Permission: The department's head (or the head of a department above it), or iam:billing:read.
  • Audited as: iam:billing:read, with metadata.via department-head or permission.
  • Errors: ACCESS_DENIED (403) for anyone else; NOT_FOUND for a department outside the tenant.
Input

Prop

Type

Returns

A SpendReport object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/departmentSpend" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "departmentId": "<departmentId>"
}'
Signature
iam.api.billing.departmentSpend(
  credential: CredentialInput,
  input: {
    tenantId: string;
    departmentId: string;
    period?: string;
    groupBy?: SpendGroupBy;
  },
): Promise<SpendReport>

exportSpend

Returns a spend report as a CSV file: one row per group with the amount, share, events and a column per meter's quantity.

POST/api/iam/billing/exportSpend
client.billing.exportSpend()Credential
  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.
  • Errors: as spend.

It takes the same input as spend and returns filename, contentType and body (RFC 4180, CRLF line endings, a final total row). Cells that a spreadsheet would run as a formula are prefixed with a quote.

Input

Prop

Type

Returns

A CsvExport object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/exportSpend" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.exportSpend(
  credential: CredentialInput,
  input: { tenantId: string } & SpendQuery,
): Promise<CsvExport>

exportStatement

Returns a statement as a CSV file: its lines, credit and total, then the breakdown by project, team, department and person.

POST/api/iam/billing/exportStatement
client.billing.exportStatement()Credential
  • Permission: iam:billing:read on iam/billing/statements; the statement's account must be the tenant or below it.
  • Audited as: iam:billing:read.
  • Errors: NOT_FOUND for a statement outside the tenant's subtree.

The file is named after the statement number. Department rows carry their cost center.

Input

Prop

Type

Returns

A CsvExport object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/exportStatement" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.exportStatement(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<CsvExport>

finalizeInvoice

Finalizes a draft invoice: recomputes it with the latest usage and invoice items, numbers it, seals it and emails it.

POST/api/iam/billing/finalizeInvoice
client.billing.finalizeInvoice()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/statements), called on the account or the root tenant.
  • Audited as: billing:statement-finalize and billing:statement.
  • Errors: INVALID_TRANSITION (409) for an invoice that is not a draft, or a draft with nothing left to bill.

The invoice then consumes its credit, marks its invoice items invoiced, its subscriptions' advance months billed, and its coupons used. An invoice with nothing to pay is paid on issue.

Input

Prop

Type

Returns

A StatementSummary object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/finalizeInvoice" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.finalizeInvoice(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<StatementSummary>

getProfile

Returns the billing profile of the tenant (or of targetTenantId below it) and the account that pays for it.

POST/api/iam/billing/getProfile
client.billing.getProfile()Credential
  • Permission: iam:billing:read on iam/billing/profile.
  • Audited as: iam:billing:read.

account.inherited is true when an ancestor pays; profile is null without a profile of its own.

Input

Prop

Type

Returns

A ProfileView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/getProfile" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.getProfile(
  credential: CredentialInput,
  input: { tenantId: string; targetTenantId?: string },
): Promise<ProfileView>

getStatement

Returns one statement with its lines, credit, breakdown and bill-to details, re-checking its content hash.

POST/api/iam/billing/getStatement
client.billing.getStatement()Credential
  • Permission: iam:billing:read on iam/billing/statements; the statement's account must be the tenant or below it.
  • Audited as: iam:billing:read.
  • Errors: NOT_FOUND for a statement outside the tenant's subtree.

verified is false when the stored content no longer matches the hash computed at issue. overdue is true for a finalized statement past its due date.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/getStatement" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.getStatement(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<{
  total: number;
  overdue: boolean;
  verified: boolean;
  number: string;
  status: InvoiceStatus;
  issuedAt: number;
  dueAt: number;
  hash: string;
  payments?: InvoicePayment[];
  amountPaidMicros?: number;
  creditNotesMicros?: number;
  invoiceItemIds?: string[];
  advanceBilled?: { subscriptionId: string; period: string }[];
  carryForward?: { creditId: string; amountMicros: number };
  reminders?: { days: number; at: number; recipients: number }[];
  paidAt?: number;
  paidBy?: string;
  paymentReference?: string;
  voidedAt?: number;
  voidedBy?: string;
  voidReason?: string;
  markedUncollectibleAt?: number;
  id: string;
  tenantId: string;
  uniqueKey?: string;
  period: string;
  currency: string;
  periodStart: number;
  periodEnd: number;
  lines: StatementLine[];
  billingReason?: 'period' | 'subscription' | 'manual';
  subtotalMicros: number;
  coupons?: { discountId: string; code: string; name: string; amountMicros: number }[];
  discount?: { percent: number; amountMicros: number };
  commitment?: { minimumMicros: number; trueUpMicros: number };
  creditsMicros: number;
  creditsApplied: { creditId: string; amountMicros: number }[];
  tax?: { label: string; ratePercent: number; amountMicros: number };
  totalMicros: number;
  breakdown: {
    tenants: StatementAllocation[];
    teams: StatementAllocation[];
    departments: (StatementAllocation & { costCenter?: string })[];
    identities: StatementAllocation[];
  };
  billTo: {
    name: string;
    companyName?: string;
    taxId?: string;
    address?: string;
    purchaseOrder?: string;
    costCenter?: string;
    emails: string[];
  };
}>

getTerms

Returns the contract terms of the tenant's billing account: discount, minimum monthly commitment and tax.

POST/api/iam/billing/getTerms
client.billing.getTerms()Credential
  • Permission: iam:billing:read on iam/billing/terms.
  • Audited as: iam:billing:read.

When an ancestor pays for the tenant the result only says inherited: true; without terms it lists none.

Input

Prop

Type

Returns

A TermsView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/getTerms" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.getTerms(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<TermsView>

grantCredit

Grants credit to a billing account, which its statements draw on, earliest expiry first.

POST/api/iam/billing/grantCredit
client.billing.grantCredit()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/credits).
  • Audited as: billing:credit-grant.
  • Errors: INVALID_INPUT when the tenant is not a billing account (an organization or a tenant with a profile), for an amount of 0, or an expiresAt in the past.
await iam.api.billing.grantCredit(root, { tenantId: acmeId, amount: 100, reason: 'Launch promotion' });
Input

Prop

Type

Returns

A CreditView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/grantCredit" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "amount": 1,
  "reason": "<reason>"
}'
Signature
iam.api.billing.grantCredit(
  credential: CredentialInput,
  input: { tenantId: string; amount: number; reason: string; expiresAt?: number },
): Promise<CreditView>

listBudgets

Lists the tenant's budgets with their spend, projection and the thresholds reached in the current window.

POST/api/iam/billing/listBudgets
client.billing.listBudgets()Credential
  • Permission: iam:billing:read on iam/billing/budgets.
  • Audited as: iam:billing:read.
Input

Prop

Type

Returns

An array of BillingBudgetView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listBudgets" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listBudgets(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<BillingBudgetView[]>

listCoupons

Lists the platform's coupons, newest first, with their redemptions.

POST/api/iam/billing/listCoupons
client.billing.listCoupons()Credential
  • Permission: Root administrators only (iam:billing:read on iam/billing/coupons), called on the root tenant.
  • Audited as: iam:billing:read.

active is false for deactivated coupons, those past redeemBy, and those out of redemptions.

Input

Prop

Type

Returns

An array of CouponView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listCoupons" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listCoupons(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<CouponView[]>

listCreditNotes

Lists the credit notes of the billing accounts in the tenant's subtree, or of one statementId, newest first.

POST/api/iam/billing/listCreditNotes
client.billing.listCreditNotes()Credential
  • Permission: iam:billing:read on iam/billing/statements.
  • Audited as: iam:billing:read.
Input

Prop

Type

Returns

An array of CreditNoteView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listCreditNotes" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listCreditNotes(
  credential: CredentialInput,
  input: { tenantId: string; statementId?: string },
): Promise<CreditNoteView[]>

listCredits

Lists the credit of the tenant's billing account with the available balance.

POST/api/iam/billing/listCredits
client.billing.listCredits()Credential
  • Permission: iam:billing:read on iam/billing/credits.
  • Audited as: iam:billing:read.

When an ancestor pays for the tenant the list is empty and inherited is true.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listCredits" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listCredits(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  accountId: string;
  inherited: boolean;
  balanceMicros: number;
  balance: number;
  credits: CreditView[];
}>

listDiscounts

Lists the discounts (redeemed coupons) of the billing accounts in the tenant's subtree.

POST/api/iam/billing/listDiscounts
client.billing.listDiscounts()Credential
  • Permission: iam:billing:read on iam/billing/discounts.
  • Audited as: iam:billing:read.

appliedInvoices counts the invoices a discount reduced; active says whether it applies to this month's invoice.

Input

Prop

Type

Returns

An array of DiscountView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listDiscounts" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listDiscounts(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<DiscountView[]>

listInvoiceItems

Lists invoice items of the billing accounts in the tenant's subtree, newest first, optionally by status.

POST/api/iam/billing/listInvoiceItems
client.billing.listInvoiceItems()Credential
  • Permission: iam:billing:read on iam/billing/invoice-items.
  • Audited as: iam:billing:read.
  • Errors: INVALID_INPUT for a status other than pending or invoiced.

pending items wait for the account's next invoice (or the one for their period); invoiced items carry the statementId that billed them. source: 'proration' items come from subscription changes.

Input

Prop

Type

Returns

An array of InvoiceItemView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listInvoiceItems" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listInvoiceItems(
  credential: CredentialInput,
  input: { tenantId: string; status?: BillingInvoiceItem['status'] },
): Promise<InvoiceItemView[]>

listMeters

Lists the meters that reach the tenant with the price that applies to its billing account this month.

POST/api/iam/billing/listMeters
client.billing.listMeters()Credential
  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.

scope is platform for root meters; inherited marks meters an ancestor defines.

Input

Prop

Type

Returns

An array of MeterView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listMeters" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listMeters(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<MeterView[]>

listPlans

Lists the platform's plans with their fees, seats and meter prices.

POST/api/iam/billing/listPlans
client.billing.listPlans()Credential
  • Permission: iam:billing:read on iam/billing/plans.
  • Audited as: iam:billing:read.

Tenants see plans that are not archived. Root administrators on the root tenant also get subscribers (live subscriptions) and, with includeArchived, archived plans. Amounts are in currency units.

Input

Prop

Type

Returns

An array of PlanView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listPlans" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listPlans(
  credential: CredentialInput,
  input: { tenantId: string; includeArchived?: boolean },
): Promise<PlanView[]>

listPrices

Returns a meter's rate card as the tenant may see it, and the entry that prices its account this month.

POST/api/iam/billing/listPrices
client.billing.listPrices()Credential
  • Permission: iam:billing:read on iam/billing/meters/{key}.
  • Audited as: iam:billing:read.
  • Errors: NOT_FOUND when no meter with the key reaches the tenant.

Entries for the tenant, its ancestors (list prices) and tenants below it are listed, newest first; negotiated prices for other organizations are not.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listPrices" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "meter": "<meter>"
}'
Signature
iam.api.billing.listPrices(
  credential: CredentialInput,
  input: { tenantId: string; meter: string },
): Promise<{ effective?: PriceView | undefined; meter: MeterView; entries: PriceView[] }>

listStatements

Lists the statements of the billing accounts in the tenant's subtree (all of them for the root), newest month first.

POST/api/iam/billing/listStatements
client.billing.listStatements()Credential
  • Permission: iam:billing:read on iam/billing/statements.
  • Audited as: iam:billing:read.

Filter by status (finalized, paid, void) or period.

Input

Prop

Type

Returns

An array of StatementSummary.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listStatements" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listStatements(
  credential: CredentialInput,
  input: { tenantId: string; status?: BillingStatement['status']; period?: string },
): Promise<StatementSummary[]>

listSubscriptions

Lists the subscriptions of the billing accounts in the tenant's subtree, newest first.

POST/api/iam/billing/listSubscriptions
client.billing.listSubscriptions()Credential
  • Permission: iam:billing:read on iam/billing/subscriptions.
  • Audited as: iam:billing:read.

Ended subscriptions are left out unless includeEnded. status is trialing, active or ended; billedAdvance lists the months already billed in advance.

Input

Prop

Type

Returns

An array of SubscriptionView.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listSubscriptions" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listSubscriptions(
  credential: CredentialInput,
  input: { tenantId: string; includeEnded?: boolean },
): Promise<SubscriptionView[]>

listUsage

Lists raw usage events recorded in the tenant for a month, newest first.

POST/api/iam/billing/listUsage
client.billing.listUsage()Credential
  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.

Only the tenant itself (not its subtree), at most limit (1 to 500, default 100) events, optionally for one meter or identity. Events are kept usageRetentionDays after their month; reports use daily roll-ups and outlive them.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/listUsage" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.listUsage(
  credential: CredentialInput,
  input: {
    tenantId: string;
    period?: string;
    meter?: string;
    identityId?: string;
    limit?: number;
  },
): Promise<{
  tenantId: string;
  period: string;
  events: {
    idempotencyKey?: string | undefined;
    meterId: string;
    meter: string;
    quantity: number;
    costMicros?: number;
    identityId?: string;
    agentId?: string;
    teamIds: string[];
    departmentId?: string;
    tags?: Record<string, string>;
    occurredAt: number;
    period: string;
    day: string;
    recordedAt: number;
    recordedBy: string;
    sourceId?: string;
    id: string;
    tenantId: string;
  }[];
}>

markPaid

Marks a finalized or uncollectible statement paid by recording a manual payment of the amount due, with an optional reference.

POST/api/iam/billing/markPaid
client.billing.markPaid()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/statements), called on the account or the root tenant.
  • Audited as: billing:statement-paid.
  • Errors: INVALID_TRANSITION (409) for a draft, paid or void statement.
Input

Prop

Type

Returns

A StatementSummary object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/markPaid" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.markPaid(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string; reference?: string },
): Promise<StatementSummary>

markUncollectible

Writes a finalized invoice off as uncollectible; a later payment still settles it.

POST/api/iam/billing/markUncollectible
client.billing.markUncollectible()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/statements).
  • Audited as: billing:statement-uncollectible.
  • Errors: INVALID_TRANSITION (409) for an invoice that is not finalized.

Uncollectible invoices get no payment reminders.

Input

Prop

Type

Returns

A StatementSummary object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/markUncollectible" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.markUncollectible(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<StatementSummary>

mySpend

Reports the caller's own spend: their usage and that of the agents they sponsor, with the budgets set on them.

POST/api/iam/billing/mySpend
client.billing.mySpend()Credential
  • Permission: The caller's own session in their own tenant.
  • Audited as: not audited.
  • Errors: ACCESS_DENIED (403) for another tenant; INVALID_INPUT for a groupBy other than meter, day, agent, tenant or tag:{name}.
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/mySpend" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.mySpend(
  credential: CredentialInput,
  input: { tenantId: string; period?: string; groupBy?: SpendGroupBy },
): Promise<SpendReport & { budgets: BudgetStatus[] }>

previewStatement

Builds the statement a billing account would receive for a month (the current one so far by default), without issuing it.

POST/api/iam/billing/previewStatement
client.billing.previewStatement()Credential
  • Permission: iam:billing:read on iam/billing/statements.
  • Audited as: iam:billing:read.
  • Errors: INVALID_INPUT when the tenant is not a billing account.
Input

Prop

Type

Returns

A StatementDraft object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/previewStatement" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.previewStatement(
  credential: CredentialInput,
  input: { tenantId: string; period?: string },
): Promise<StatementDraft>

quote

Prices a quantity of a meter as a month total for the tenant's billing account.

POST/api/iam/billing/quote
client.billing.quote()Credential
  • Permission: iam:billing:read on iam/billing/meters/{key}.
  • Audited as: iam:billing:read.
  • Errors: NOT_FOUND for an unknown meter; INVALID_INPUT for a reported meter or a negative quantity.

Tiers and free units apply as they would to the account's month total; unpriced is true without a price.

Input

Prop

Type

Returns

One of object | object.

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/quote" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "meter": "<meter>",
  "quantity": 1
}'
Signature
iam.api.billing.quote(
  credential: CredentialInput,
  input: { tenantId: string; meter: string; quantity: number; period?: string },
): Promise<| {
      price: PriceView;
      meter: string;
      quantity: number;
      period: string;
      currency: string;
      amountMicros: number;
      amount: number;
    }
  | {
      unpriced: true;
      meter: string;
      quantity: number;
      period: string;
      currency: string;
      amountMicros: number;
      amount: number;
    }>

record

Records usage of a meter in the tenant, attributed to an identity, their teams and department, with optional tags.

POST/api/iam/billing/record
client.billing.record()Credential
  • Permission: iam:billing:record on iam/billing/meters/{key}.
  • Audited as: iam:billing:record.
  • Errors: NOT_FOUND for an unknown meter or an identity outside the tenant and its ancestors; METER_ARCHIVED (409); BILLING_PERIOD_CLOSED (409) for a month already invoiced; SPEND_LIMIT_REACHED (402) with enforceBudgets under a spent enforced budget; CONFLICT (409) for an idempotency key used on another meter; INVALID_INPUT for a negative quantity, cost on a rate-card meter or none on a reported one, or occurredAt more than five minutes ahead or a year back.

idempotencyKey makes retries safe: a repeat returns the first receipt with duplicate: true. teamId attributes the usage to one team instead of the person's own. Server code records without an audit event per call through iam.billing.record, the usual choice for metering.

await client.billing.record({
  tenantId,
  meter: 'api-calls',
  quantity: 1,
  identityId,
  tags: { endpoint: 'search' },
  idempotencyKey: requestId,
});
Input

Prop

Type

Returns

A UsageReceipt object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/record" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "meter": "<meter>"
}'
Signature
iam.api.billing.record(
  credential: CredentialInput,
  input: UsageInput & { enforceBudgets?: boolean },
): Promise<UsageReceipt>

recordMany

Records up to 100 usage events in one transaction, all or nothing.

POST/api/iam/billing/recordMany
client.billing.recordMany()Credential
  • Permission: iam:billing:record on iam/billing in the call's tenant.
  • Audited as: iam:billing:record.
  • Errors: as record; INVALID_INPUT for an event outside the tenant's subtree or more than 100 events.
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/recordMany" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "events": {
    "meter": "<meter>"
  }
}'
Signature
iam.api.billing.recordMany(
  credential: CredentialInput,
  input: {
    tenantId: string;
    events: Omit<UsageInput, 'tenantId'> & { tenantId?: string }[];
  },
): Promise<{ receipts: UsageReceipt[] }>

recordPayment

Records a payment against a finalized or uncollectible invoice.

POST/api/iam/billing/recordPayment
client.billing.recordPayment()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/statements), called on the account or the root tenant.
  • Audited as: billing:payment.
  • Errors: INVALID_TRANSITION (409) for a draft, paid or void invoice; INVALID_INPUT for an amount of 0 or a receivedAt in the future.

amount (currency units) defaults to the amount due; method (default manual) and reference describe it. A partial payment leaves the rest due; once payments and credit notes cover the invoice it is paid. A payment above the amount due keeps the excess as account credit (payment.overpaymentMicros, payment.creditId). Payment processors report payments through iam.billing.recordPayment({ statementId | number, amount, idempotencyKey }) instead, where the idempotency key makes webhook redelivery safe.

await iam.api.billing.recordPayment(root, {
  tenantId: acmeId,
  statementId,
  amount: 300,
  method: 'bank_transfer',
  reference: 'wire-88213',
});
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/recordPayment" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.recordPayment(
  credential: CredentialInput,
  input: {
    tenantId: string;
    statementId: string;
    amount?: number;
    method?: string;
    reference?: string;
    receivedAt?: number;
  },
): Promise<{ statement: StatementSummary; payment: InvoicePayment }>

redeemCoupon

Redeems a coupon code for the billing account.

POST/api/iam/billing/redeemCoupon
client.billing.redeemCoupon()Credential

Used inBilling and spend

  • Permission: iam:billing:manage on iam/billing/discounts in the account.
  • Audited as: billing:coupon-redeem.
  • Errors: CONFLICT (409) when the account already redeemed the coupon; NOT_FOUND for a code that is unknown, inactive, past redeemBy or out of redemptions (all answer the same); INVALID_INPUT when the tenant is not a billing account.

The resulting discount applies to the account's invoices after the contract discount, in the order codes were redeemed. Voiding an invoice gives a one-off discount back.

Input

Prop

Type

Returns

A DiscountView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/redeemCoupon" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "code": "<code>"
}'
Signature
iam.api.billing.redeemCoupon(
  credential: CredentialInput,
  input: { tenantId: string; code: string },
): Promise<DiscountView>

removeDiscount

Ends a billing account's discount now.

POST/api/iam/billing/removeDiscount
client.billing.removeDiscount()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/discounts), called on the account.
  • Audited as: billing:discount-remove.
  • Errors: INVALID_TRANSITION (409) for a discount that has already ended.
Input

Prop

Type

Returns

A DiscountView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/removeDiscount" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "discountId": "<discountId>"
}'
Signature
iam.api.billing.removeDiscount(
  credential: CredentialInput,
  input: { tenantId: string; discountId: string },
): Promise<DiscountView>

renderInvoice

Returns an invoice as a standalone HTML page to print or save as PDF.

POST/api/iam/billing/renderInvoice
client.billing.renderInvoice()Credential
  • Permission: iam:billing:read on iam/billing/statements.
  • Audited as: iam:billing:read.
  • Errors: NOT_FOUND for an invoice outside the tenant's subtree.

The result is { filename, contentType, body }. The page names the issuer (the billing.issuer option: name, address, tax ID, contact, and paymentInstructions under the totals), the bill-to details, every line with its tier sub-lines, service period and proration, then discounts, coupons, credit, tax, payments, credit notes and the amount due. It has no scripts and only inline styles, so it can be served with a strict content security policy.

Input

Prop

Type

Returns

A InvoiceDocument object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/renderInvoice" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>"
}'
Signature
iam.api.billing.renderInvoice(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<InvoiceDocument>

resumeSubscription

Undoes a cancellation at the end of the month before it takes effect.

POST/api/iam/billing/resumeSubscription
client.billing.resumeSubscription()Credential
  • Permission: iam:billing:manage on iam/billing/subscriptions (self-serve plans), or a root administrator.
  • Audited as: billing:subscription-resume.
  • Errors: INVALID_TRANSITION (409) for a subscription that is not set to cancel, or has ended.
Input

Prop

Type

Returns

A SubscriptionView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/resumeSubscription" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "subscriptionId": "<subscriptionId>"
}'
Signature
iam.api.billing.resumeSubscription(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string },
): Promise<SubscriptionView>

revokeCredit

Withdraws what is left of a credit.

POST/api/iam/billing/revokeCredit
client.billing.revokeCredit()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/credits).
  • Audited as: billing:credit-revoke.
  • Errors: INVALID_TRANSITION (409) for a credit already revoked.
Input

Prop

Type

Returns

A CreditView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/revokeCredit" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "creditId": "<creditId>"
}'
Signature
iam.api.billing.revokeCredit(
  credential: CredentialInput,
  input: { tenantId: string; creditId: string },
): Promise<CreditView>

setPrice

Sets or removes a rate-card price for a meter the tenant defines: its list price, or a negotiated price for a tenant below it.

POST/api/iam/billing/setPrice
client.billing.setPrice()Credential

Used inBilling and spend

  • Permission: iam:billing:manage on iam/billing/meters/{key} in the defining tenant.
  • Audited as: billing:price.
  • Errors: NOT_FOUND when the tenant does not define the meter; INVALID_INPUT for a reported meter, a target outside the subtree, malformed tiers, or effectiveFrom more than 12 months back; BILLING_PERIOD_CLOSED (409) when a month from effectiveFrom on is already invoiced for the tenants it reaches.

price takes currency units: per-unit (unitAmount), graduated or volume (tiers of upTo and unitAmount, optional flatAmount, the last upTo: null), or package (packageSize, packageAmount), each with an optional includedQuantity. effectiveFrom (default this month) starts the price; price: null removes that entry.

await iam.api.billing.setPrice(root, {
  tenantId: rootTenantId,
  meter: 'api-calls',
  targetTenantId: acmeId,
  price: { model: 'per-unit', unitAmount: 0.0003 },
  note: 'Enterprise agreement',
});
Input

Prop

Type

Returns

One of null | PriceView; the object form has these fields:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/setPrice" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "meter": "<meter>",
  "price": {}
}'
Signature
iam.api.billing.setPrice(
  credential: CredentialInput,
  input: {
    tenantId: string;
    meter: string;
    targetTenantId?: string;
    effectiveFrom?: string;
    price: Record<string, unknown> | null;
    note?: string;
  },
): Promise<PriceView | null>

setProfile

Creates or updates a billing profile: company, billing emails, tax ID, address, purchase order, cost center, payment terms.

POST/api/iam/billing/setProfile
client.billing.setProfile()Credential
  • Permission: iam:billing:manage on iam/billing/profile; a new profile below an organization is created from an ancestor with targetTenantId.
  • Audited as: billing:profile.
  • Errors: ACCESS_DENIED (403) when a tenant below an organization creates its own; INVALID_INPUT on the root tenant or for more than 10 emails.

A profile below an organization makes that tenant a billing account of its own, a decision for its parent; afterwards the tenant's own billing managers may keep it up to date. null clears a field.

Input

Prop

Type

Returns

A ProfileView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/setProfile" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.setProfile(
  credential: CredentialInput,
  input: {
    tenantId: string;
    targetTenantId?: string;
    companyName?: string | null;
    billingEmails?: string[];
    taxId?: string | null;
    address?: string | null;
    purchaseOrder?: string | null;
    costCenter?: string | null;
    paymentTermsDays?: number | null;
  },
): Promise<ProfileView>

setTerms

Sets a billing account's contract terms: a discount off the subtotal, a minimum monthly commitment, and the tax invoices add.

POST/api/iam/billing/setTerms
client.billing.setTerms()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/terms), called on the account.
  • Audited as: billing:terms.
  • Errors: INVALID_INPUT when the tenant is not a billing account, for a percentage outside 0 to 100, or a negative commitment.

Invoices issued from then on take the subtotal of their lines, subtract discountPercent, add the shortfall below minimumCommitment (currency units per month, monthly invoices only) as a true-up, subtract coupons and credit, and add taxRatePercent (labelled taxLabel, Tax by default) on the rest. null clears a term. Spend reports stay at rate-card prices.

await iam.api.billing.setTerms(root, {
  tenantId: acmeId,
  discountPercent: 10,
  minimumCommitment: 1000,
  taxRatePercent: 20,
  taxLabel: 'VAT',
});
Input

Prop

Type

Returns

A TermsView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/setTerms" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.setTerms(
  credential: CredentialInput,
  input: {
    tenantId: string;
    discountPercent?: number | null;
    minimumCommitment?: number | null;
    taxRatePercent?: number | null;
    taxLabel?: string | null;
  },
): Promise<TermsView>

spend

Reports the spend of the tenant and every tenant below it for a month, grouped and filtered.

POST/api/iam/billing/spend
client.billing.spend()Credential

Used inBilling and spend

  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.
  • Errors: NOT_FOUND for a team, department or sub-tenant outside the scope; INVALID_INPUT for an unknown groupBy or a malformed month.

groupBy is meter (default), identity, agent, team, department, tenant, day or tag:{name}. Filters: meter, identityId, teamId, departmentId, subTenantId, rollUp: false (no sub-teams or sub-departments) and billableOnly (leave out chargeback meters). Each row has costMicros, amount, share (percent) and quantities per meter; the current month carries a linear forecast. For showback, shareUnattributed: true (grouped by identity, agent, team or department) spreads the unattributed row over the other groups by their share of spend, reported as sharedMicros per row and for the report.

const report = await iam.api.billing.spend(admin, { tenantId, groupBy: 'team' });
Input

Prop

Type

Returns

A SpendReport object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/spend" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.spend(
  credential: CredentialInput,
  input: { tenantId: string; period?: string; groupBy?: SpendGroupBy } & SpendFilters,
): Promise<SpendReport>

subscribe

Subscribes the billing account to a plan (id or key).

POST/api/iam/billing/subscribe
client.billing.subscribe()Credential

Used inBilling and spend

  • Permission: iam:billing:manage on iam/billing/subscriptions: self-serve plans for the account's billing managers, any plan for root administrators.
  • Audited as: billing:subscription.
  • Errors: ACCESS_DENIED (403) for a plan that is not self-serve, or trialDays from anyone but a root administrator; CONFLICT (409) when the account already subscribes to the plan; INVALID_TRANSITION (409) for an archived plan; INVALID_INPUT when the tenant is not a billing account.

seats defaults to 1. Outside a trial the rest of this month's advance fees and seats are invoiced at once (the result's invoice, billingReason: 'subscription'); after that each monthly invoice bills the month ahead. A trial bills nothing until it ends; the part of the month after it is billed on that month's invoice.

const { subscription, invoice } = await iam.api.billing.subscribe(orgAdmin, {
  tenantId: acmeId,
  plan: 'team',
  seats: 8,
});
Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/subscribe" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "plan": "<plan>"
}'
Signature
iam.api.billing.subscribe(
  credential: CredentialInput,
  input: { tenantId: string; plan: string; seats?: number; trialDays?: number },
): Promise<{ subscription: SubscriptionView; invoice?: StatementSummary }>

teamSpend

Reports a team's spend, with the teams below it, grouped by identity by default.

POST/api/iam/billing/teamSpend
client.billing.teamSpend()Credential
  • Permission: The team's maintainers (and those of teams above it), or iam:billing:read.
  • Audited as: iam:billing:read, with metadata.via team-maintainer or permission.
  • Errors: ACCESS_DENIED (403) for anyone else; NOT_FOUND for a team outside the tenant.
Input

Prop

Type

Returns

A SpendReport object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/teamSpend" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "teamId": "<teamId>"
}'
Signature
iam.api.billing.teamSpend(
  credential: CredentialInput,
  input: { tenantId: string; teamId: string; period?: string; groupBy?: SpendGroupBy },
): Promise<SpendReport>

trend

Returns monthly totals for the last months months (1 to 24, default 6), with the filters of spend.

POST/api/iam/billing/trend
client.billing.trend()Credential

Used inBilling and spend

  • Permission: iam:billing:read on iam/billing.
  • Audited as: iam:billing:read.
Input

Prop

Type

Returns

A SpendTrend object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/trend" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.billing.trend(
  credential: CredentialInput,
  input: { tenantId: string; months?: number } & SpendFilters,
): Promise<SpendTrend>

updateBudget

Changes a budget's name, amount, period, meters, thresholds, alerts or enforcement; its subject stays.

POST/api/iam/billing/updateBudget
client.billing.updateBudget()Credential
  • Permission: iam:billing:manage on iam/billing/budgets.
  • Audited as: billing:budget-update.
  • Errors: CONFLICT (409) for a name another budget uses; NOT_FOUND for a budget of another tenant.

meters: null counts every meter again.

Input

Prop

Type

Returns

A BillingBudgetView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/updateBudget" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "budgetId": "<budgetId>"
}'
Signature
iam.api.billing.updateBudget(
  credential: CredentialInput,
  input: { tenantId: string; budgetId: string } & Partial<
    Omit<BillingBudgetInput, 'subjectType' | 'subjectId' | 'meters'>
  > & { meters?: string[] | null },
): Promise<BillingBudgetView>

updateMeter

Renames a meter, changes its unit or description, or archives it.

POST/api/iam/billing/updateMeter
client.billing.updateMeter()Credential
  • Permission: iam:billing:manage on iam/billing/meters/{key}.
  • Audited as: billing:meter-update.
  • Errors: NOT_FOUND when the tenant does not define the key; INVALID_INPUT when changing aggregation or pricing.

An archived meter refuses new usage (METER_ARCHIVED) and keeps its history; archived: false restores it.

Input

Prop

Type

Returns

A MeterView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/updateMeter" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "key": "<key>"
}'
Signature
iam.api.billing.updateMeter(
  credential: CredentialInput,
  input: {
    tenantId: string;
    key: string;
    name?: string;
    unit?: string;
    description?: string | null;
    archived?: boolean;
  },
): Promise<MeterView>

updatePlan

Changes a plan, by id or key: name, items, description, trial, self-serve, archived.

POST/api/iam/billing/updatePlan
client.billing.updatePlan()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/plans), called on the root tenant.
  • Audited as: billing:plan-update.
  • Errors: NOT_FOUND for an unknown plan; INVALID_INPUT as for createPlan.

null clears description or trialDays. An archived plan takes no new subscriptions. Changes apply to invoices drawn up afterwards.

Input

Prop

Type

Returns

A PlanView object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/updatePlan" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "plan": "<plan>"
}'
Signature
iam.api.billing.updatePlan(
  credential: CredentialInput,
  input: {
    tenantId: string;
    plan: string;
    name?: string;
    items?: unknown[];
    description?: string | null;
    trialDays?: number | null;
    selfServe?: boolean;
    archived?: boolean;
  },
): Promise<PlanView>

updateSubscription

Changes a subscription's seats.

POST/api/iam/billing/updateSubscription
client.billing.updateSubscription()Credential
  • Permission: iam:billing:manage on iam/billing/subscriptions (self-serve plans), or a root administrator.
  • Audited as: billing:subscription-update.
  • Errors: INVALID_TRANSITION (409) for an ended subscription; INVALID_INPUT for seats outside 0 to 1000000.

Seats billed in advance for this month are prorated as invoice items on the next invoice (invoiceItems): added seats for the rest of the month, credit for removed ones. Arrears seats follow the seat history on their own.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/updateSubscription" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "subscriptionId": "<subscriptionId>",
  "seats": 1
}'
Signature
iam.api.billing.updateSubscription(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string; seats: number },
): Promise<{ subscription: SubscriptionView; invoiceItems: InvoiceItemView[] }>

voidStatement

Voids a finalized statement: its credit, invoice items, coupons and advance-billed months come back and the month reopens.

POST/api/iam/billing/voidStatement
client.billing.voidStatement()Credential
  • Permission: Root administrators only (iam:billing:manage on iam/billing/statements), called on the account or the root tenant.
  • Audited as: billing:statement-void.
  • Errors: INVALID_TRANSITION (409) for a statement already void, a draft, or one with payments or credit notes (issue a credit note instead).

Fix the usage, then close the month again: the new statement gets a new number. Credit the invoice created from a negative balance is revoked.

Input

Prop

Type

Returns

A StatementSummary object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/billing/voidStatement" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "statementId": "<statementId>",
  "reason": "<reason>"
}'
Signature
iam.api.billing.voidStatement(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string; reason: string },
): Promise<StatementSummary>

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page