Check and track usage
Check affordability before work and charge completed usage safely.
Use check to ask whether a customer can afford an operation. Use track to charge it and record usage. Both run on your server and require an API key with events:ingest permission.
The examples use the deployed catalog from the quickstart, where api-call costs one credit per request. Include the generated _orbyte.ts in your TypeScript project to type feature names. You do not need to import the config for fixed prices. Set ORBYTE_API_KEY in your server environment. The SDK uses https://api.orbytelabs.com automatically.
Check before work
import { check } from "@orbytelabs/credit";
const access = await check("api-call", "customer_123");
if (!access.allowed) {
throw new Error("Add credits before making another request.");
}For a wallet with 100 credits, the result is:
{
"allowed": true,
"creditKey": "credits",
"amountNanocredits": "1000000000",
"balanceNanocredits": "100000000000"
}The amount is the charge for the supplied feature and quantity, after any included plan allowance. balanceNanocredits is the current balance for that credit type. An unaffordable check returns allowed: false in a successful response; it does not throw an insufficient-balance error.
When a plan covers the feature, the response can include limit with the allowance and current usage. A denial can include reason: "feature_limit_exceeded" or reason: "subscription_inactive". Check allowed for every response; do not rely on a reason being present.
Checks do not create wallets, deduct credits, reserve funds, or record usage. A missing wallet has a zero balance. Checking a free feature can return allowed: true for a missing wallet without creating it.
Track the charge
After the customer uses the feature, record the event:
import { track } from "@orbytelabs/credit";
const result = await track("api-call", "customer_123", {
id: "request_123:api-call",
});{
"status": "accepted",
"transactionId": "TRANSACTION_ID",
"amountNanocredits": "1000000000"
}Credit checks the balance and applies the debit in one transaction. Concurrent requests cannot spend the same credits twice or take the balance below zero.
An allowed check is a snapshot. Another request can spend the balance, or the price or usage period can change, before you track. track checks again and throws OrbyteApiError with code insufficient_balance when the wallet cannot cover the charge. Decide how your application handles that case, especially when provider work has already incurred a cost.
A rejected charge creates no usage transaction and does not lock a usage period. It can provision an empty wallet so you can fund it and retry.
Tracking also enforces plan allowances. A blocked limit or inactive subscription returns 409 with feature_limit_exceeded or subscription_inactive. Adding credits alone does not resolve those denials. Included usage and credit deductions commit together, so a failed charge does not consume the allowance.
Quantities
For fixed-price features, send the same quantity to both calls:
import { check, track } from "@orbytelabs/credit";
const quantity = 5;
const access = await check("api-call", "customer_123", { quantity });
if (!access.allowed) throw new Error("Not enough credits.");
// Perform the five requests here.
await track("api-call", "customer_123", {
id: "batch_123:api-calls",
quantity,
});Omitted quantity means 1. The SDK accepts a positive safe integer as a number or bigint. For callback pricing and period features, quantity must be 1.
Feature objects from your config remain supported. For callback prices, pass config.features.inference with its typed data so the SDK can run the pricing function on your server. Generated types reject callback names in string-based calls. See prices from event data.
Event IDs and retries
Choose one event ID for each billable occurrence, such as a job ID plus a feature key. Keep it stable when retrying that occurrence.
| Result | Meaning | Balance effect |
|---|---|---|
accepted | New usage event | Deducts the calculated charge, which can be zero. |
duplicate | Identical retry of an existing event ID | Returns the original transaction without another debit. |
deduplicated | New event ID for a feature already charged in this period | Records a zero-charge transaction. |
Event IDs are unique across the selected application. Retrying an ID with a different identity, feature, quantity, entity ID, or resolved callback amount returns 409 idempotency_conflict.
If you omit id, the SDK creates a UUID and reuses it on its automatic transport retries. A separate call to track creates a new UUID. Supply your own ID when a job, request handler, or queue consumer can run again.
An identical retry returns the original charge even after a fixed price or period changes. For callback prices, preserve the original data and calculation when retrying; a different resolved amount conflicts with the original event.
check takes no event ID and creates no billable occurrence. Retrying a check simply reads the current state again. See errors and retries for failure handling.
Periods and entities
For period features, check includes period boundaries. If the wallet or entity was already charged for that period, it returns a zero charge. A matching track records the repeated use with status: "deduplicated" and deduplicatedFrom pointing to the first transaction.
Use the same entityId in check and track when one wallet pays for several resources. The feature must have a deduplication period to accept an entity ID.
HTTP equivalents
check calls POST /v1/check. track calls POST /v1/events. Both accept an external wallet identity and a deployed feature key. For complete request schemas and responses, use the API reference.
For agents built with AI SDK, the AI SDK integration tracks model and tool usage through telemetry.