Skip to content
Indiana
Dashboard
Docs
Browse docs / Entitlements, balances and usage

Entitlements, balances and usage

Check access, report usage, manage balances and Entities, and bill AI calls.

API v1Updated

Check access and report usage

Call checkEntitlement before running a feature and trackUsage after it succeeds. Quantities are decimal strings.

usage.tsTypeScript
import { createIndianaServer } from "@tinkerlandai/indiana-sdk/server"; const indiana = createIndianaServer({  baseUrl: process.env.INDIANA_API_URL!,  apiKey: process.env.INDIANA_API_KEY!,  scope: { projectId: process.env.INDIANA_PROJECT_ID!, environment: "sandbox" },});const featureId = process.env.INDIANA_FEATURE_ID!; async function main() {  // 1. Find or create the Customer for your signed-in user.  const { customer } = await indiana.getOrCreateCustomer({ externalUserId: "user-42" });   // 2. Check access before doing the work.  const { check } = await indiana.checkEntitlement({    customerId: customer.id,    requirement: { internal_feature_id: featureId, quantity: "1", properties: {} },  });  if (!check.allowed) return console.log("blocked:", check.reason);   // 3. Report usage after the work succeeds. Use your own event ID in production.  const { receipts } = await indiana.trackUsage({    customerId: customer.id,    command: { internal_feature_id: featureId, quantity: "1", idempotencyKey: crypto.randomUUID() },  });  console.log("recorded:", receipts[0]?.accepted);} main().catch((error) => {  console.error(error);  process.exitCode = 1;});
  • Generate and save an idempotencyKey per usage; retrying an unknown result with the same key never bills twice.
  • HTTP 200 does not mean usage was accepted: check each receipt's accepted and reason.

Reserve, then confirm

For long-running work, reserve credits first, confirm the actual quantity when it finishes, or release them on failure.

reserve.tsTypeScript
import { createIndianaServer } from "@tinkerlandai/indiana-sdk/server"; const indiana = createIndianaServer({  baseUrl: process.env.INDIANA_API_URL!,  apiKey: process.env.INDIANA_API_KEY!,  scope: { projectId: process.env.INDIANA_PROJECT_ID!, environment: "sandbox" },}); const feature = process.env.INDIANA_FEATURE_ID!; // Hold credits before long-running work; keys are persisted per job.export async function runJob(customerId: string, jobId: string, estimate: string) {  const { receipt } = await indiana.mutateBalance({    customerId,    command: {      operation: "reserve",      internal_feature_id: feature,      quantity: estimate,      properties: {},      idempotencyKey: `${jobId}:reserve`,      expiresInSeconds: 600,    },  });  if (!receipt.accepted) return { started: false as const, reason: receipt.reason };  return { started: true as const, reservationId: receipt.id };} export async function finishJob(customerId: string, jobId: string, reservationId: string, actual: string | null) {  const command = actual    ? { operation: "confirm" as const, operationId: reservationId, quantity: actual, idempotencyKey: `${jobId}:confirm` }    : { operation: "release" as const, operationId: reservationId, idempotencyKey: `${jobId}:release` };  const { receipt } = await indiana.mutateBalance({ customerId, command });  return receipt;}

Read balances and the ledger

balances.tsTypeScript
import { createIndianaServer } from "@tinkerlandai/indiana-sdk/server"; const indiana = createIndianaServer({  baseUrl: process.env.INDIANA_API_URL!,  apiKey: process.env.INDIANA_API_KEY!,  scope: { projectId: process.env.INDIANA_PROJECT_ID!, environment: "sandbox" },}); export async function readBalances(customerId: string) {  const { holdings } = await indiana.readHoldings({ customerId });  const { ledger } = await indiana.readLedger({    customerId,    query: { type: null, from: null, to: null, after: null, limit: 20 },  });  return { holdings, ledger };}
Balance management
TaskMethods
Grant or claw back creditsmanageBalanceGrant, listBalanceGrants
Correct one balanceadjustBalance, readBalanceSource
Set usagesetUsage, readUsage
Report usage in bulksubmitUsageBatch, readUsageBatch
Statements and analyticsreadCreditStatement, readUsageAnalytics, readCreditAnalytics
Meter per workspace or devicemanageUsageEntity
Assign seatsreadSeats, manageSeat

Balance management

  • Grant or claw back credits

    Methods
    manageBalanceGrant, listBalanceGrants
  • Correct one balance

    Methods
    adjustBalance, readBalanceSource
  • Set usage

    Methods
    setUsage, readUsage
  • Report usage in bulk

    Methods
    submitUsageBatch, readUsageBatch
  • Statements and analytics

    Methods
    readCreditStatement, readUsageAnalytics, readCreditAnalytics
  • Meter per workspace or device

    Methods
    manageUsageEntity
  • Assign seats

    Methods
    readSeats, manageSeat

Bill AI calls

Pass the middleware to the AI SDK's wrapLanguageModel; each call is billed by actual token usage.

ai-billing.tsTypeScript
import {  createAiUsageMiddleware,  createConfiguredAiPricing,  createIndianaServer,  type AiUsageRecord,} from "@tinkerlandai/indiana-sdk/server"; const indiana = createIndianaServer({  baseUrl: process.env.INDIANA_API_URL!,  apiKey: process.env.INDIANA_API_KEY!,  scope: { projectId: process.env.INDIANA_PROJECT_ID!, environment: "sandbox" },}); // Your own durable store for usage records and frozen quotes.declare function saveUsageRecord(record: AiUsageRecord): Promise<void>; export async function billingMiddleware(customerId: string, callId: string) {  const featureId = process.env.INDIANA_AI_FEATURE_ID!;  const pricing = await createConfiguredAiPricing(indiana, {    internal_feature_id: featureId,    customerId,  });  return createAiUsageMiddleware({    pricing,    client: indiana,    modelId: "openai/gpt-4o",    reasoning: "output",    context: () => ({ customerId, internal_feature_id: featureId, idempotencyKey: callId }),    persist: saveUsageRecord,  });}// Pass the result to wrapLanguageModel({ model, middleware }) from the AI SDK.
  • Use a new idempotencyKey per model call and store records in persist to recover after failures.
  • Write modelId as provider/model; your backend needs access to https://models.dev for model prices.

Related operations

Entitlements and product presentation (4)
checkEntitlement, resolveOffering, resolvePaywall, readHoldings
Balances, usage and ledger (16)
readBalanceSource, adjustBalance, readUsage, setUsage, manageBalanceGrant, listBalanceGrants, manageUsageEntity, readUsageAnalytics, readCreditAnalytics, submitUsageBatch, readUsageBatch, readLedger, readCreditStatement, quoteBalance, trackUsage, mutateBalance
Seats (2)
readSeats, manageSeat
AI pricing (1)
readAiPricing

Related: Paths, credentials and scopes in All operations