AI SDK advanced
Connect access checks and BillingTelemetry yourself.
Use this mode when you need to own the access checks and telemetry lifetime, or integrate with generateText and streamText. For automatic setup, use TrackedToolLoopAgent.
The Credit integration connects AI SDK 6 agents to wallet balances. Your agent calls check before work starts. BillingTelemetry tracks completed model steps and successful priced tool calls.
This guide uses @orbytelabs/credit/ai-sdk with AI SDK 6. The integration uses experimental_telemetry.integrations. It requires Node.js 24+ and an AI Gateway model that returns providerMetadata.gateway.cost.
Complete the SDK setup first. Keep ORBYTE_API_KEY and AI_GATEWAY_API_KEY on the server. The Credit SDK uses https://api.orbytelabs.com automatically. Your billing key selects the Credit application; your Gateway key authenticates model requests. See AI Gateway setup for Gateway credentials.
Install the SDK and integration dependencies:
pnpm add @orbytelabs/credit ai@^6 zodDefine your agent
Create the agent for each request, using that request's wallet identity and billing telemetry. This keeps concurrent customers' usage separate.
import { ToolLoopAgent, tool } from "ai";
import { z } from "zod";
import { check } from "@orbytelabs/credit";
import { BillingTelemetry } from "@orbytelabs/credit/ai-sdk";
export function createAgent(identity: string, billing: BillingTelemetry) {
return new ToolLoopAgent({
model: "openai/gpt-5.4-mini",
instructions:
"Use the weather tool when asked about weather. Explain that its results are demo data.",
experimental_telemetry: { integrations: billing },
prepareStep: async () => {
billing.assert();
const access = await check("inference", identity, {
quantity: 1_000_000,
});
if (!access.allowed) throw new Error("Inference usage is not allowed.");
return {};
},
tools: {
weather: tool({
description: "Get demo weather conditions for a location",
metadata: { orbyte: { cost: "0.02" } },
inputSchema: z.object({ location: z.string() }),
execute: async ({ location }) => {
const access = await check("weather", identity);
if (!access.allowed) throw new Error("Tool usage is not allowed.");
return { location, temperature: 28, condition: "sunny" };
},
}),
},
});
}The weather tool costs 0.02 credits per successful call. metadata.orbyte.cost accepts a decimal string or number. Tools without this metadata, tools priced at zero, and failed tool calls do not produce tool charges.
The inference check above tests a threshold of $0.001, expressed as 1_000_000 nanodollars. With the catalog below, that costs 0.001 credits. This is a starting-balance threshold, not an estimate or limit on the next model call. check does not reserve funds. A larger actual cost or concurrent usage can still cause the later charge to fail.
If you add plan allowances, check also evaluates their limits and subscription state. A denied check can mean a reached limit or inactive subscription, not just an insufficient credit balance.
Deploy the agent's catalog
aiSdkBilling reads the agent's tool metadata and creates the credit and feature declarations.
Use this agent catalog for the recipe below. It defines inference with fixed pricing in agent-credit, while the general quickstart defines a callback-priced inference feature in credits. Deploying both configs to the same application updates that same feature key. Choose this config instead of the quickstart config for this recipe. If the application already uses the quickstart catalog, update every inference caller to send nanodollar quantities before switching catalogs. The quickstart's callback data is not compatible with this agent catalog.
import { aiSdkBilling, BillingTelemetry } from "@orbytelabs/credit/ai-sdk";
import { createAgent } from "./src/agent.js";
const billing = new BillingTelemetry({ identity: "catalog" });
export default aiSdkBilling(createAgent("catalog", billing));The factory only defines the agent. Loading this config does not run the model, execute tools, or create a wallet for catalog. Keep generation and top-ups outside modules that the config imports.
pnpm exec orbyte deploy --dry-run
pnpm exec orbyte deployUse a setup key with catalog read and write permissions for deployment. The CLI reference covers configuration options, generated keys, and Production deployment.
The helper deploys this catalog for the example:
| Entry | Configuration |
|---|---|
| Credit type | agent-credit, named Agent credit. |
inference feature | One credit per 1_000_000_000 nanodollars of Gateway cost, equal to one credit per USD. |
weather feature | 0.02 credits per successful call. |
inference is reserved for model usage. Do not give a priced tool this name. The helper uses tool names as feature keys and includes only tools with a positive metadata.orbyte.cost. It sets no usage-period deduplication.
The helper fixes the credit key and the inference conversion above. To choose a different inference rate, define your own pricing configuration with the same inference feature and nanodollar quantity unit. Telemetry sends quantities; the deployed catalog sets the charge.
Fund the wallet
Fund the agent-credit balance before running paid work.
import { credit, topup } from "@orbytelabs/credit";
await topup("customer_123", credit("agent-credit")(10), {
idempotencyKey: "agent_credit_purchase_123",
});Run this when granting or selling credits, using the same key when retrying that deposit. Do not top up on every agent request. See wallets and top-ups.
Generate and finish billing
Create one BillingTelemetry instance for each run. Keep it alive until generation and its billing callbacks finish.
import { BillingTelemetry } from "@orbytelabs/credit/ai-sdk";
import { createAgent } from "./agent.js";
export async function reply(identity: string, prompt: string) {
using billing = new BillingTelemetry({
identity,
runId: crypto.randomUUID(),
});
const agent = createAgent(identity, billing);
const result = await agent.generate({ prompt });
return result.text;
}The using declaration calls billing.assert() when the function exits. A failed final charge therefore rejects reply before it returns the answer. The prepareStep callback also calls assert() before each new model step to stop after an earlier billing failure.
BillingTelemetry queues billing errors by default. AI SDK catches errors thrown by telemetry integrations, so errorMode: "throw" alone does not guarantee that your generation rejects. Always check errors after generation with using or an explicit billing.assert(). The optional billing.prepareStep callback performs this check only in throw mode; the example uses an explicit check in both modes.
assert() throws a single stored failure or an AggregateError for several failures, then clears the error queue. That queue stores errors, not pending usage for later replay. Disposal does not retry failed charges.
Stream a response
Keep the billing scope open until you consume the stream and await completion.
import { BillingTelemetry } from "@orbytelabs/credit/ai-sdk";
import { createAgent } from "./agent.js";
export async function streamReply(
identity: string,
prompt: string,
write: (text: string) => void,
) {
using billing = new BillingTelemetry({
identity,
runId: crypto.randomUUID(),
});
const agent = createAgent(identity, billing);
const result = await agent.stream({ prompt });
for await (const chunk of result.textStream) {
write(chunk);
}
await result.text;
}Do not create using billing inside an HTTP handler and immediately return a still-running stream. That closes the billing scope before the final charges exist. The code consuming the stream must own the telemetry lifetime and handle any billing error after completion. Content already sent to the client cannot be recalled if the final charge fails.
An interrupted model step may not return the cost metadata needed for billing. This adapter records completed steps for which Gateway supplies a cost; it cannot reconstruct missing provider costs after an interrupted stream.
What gets tracked
After each completed model step, telemetry reads the Gateway USD cost, rounds it up to a whole nanodollar, and tracks that quantity against inference. Zero-cost steps do not produce an event. Missing or invalid cost metadata records a billing error; the adapter does not estimate a charge from token counts.
After each successful priced tool call, it tracks quantity 1 against the tool name. The deployed feature price determines the charge. Redeploy after changing tool prices, and do not manually track the same model or tool usage again.
| Event | ID when runId is supplied |
|---|---|
| Model step | {runId}:{stepNumber} |
| Tool call | {runId}-{toolCallId} |
Use a unique run ID for each new execution. Reusing a run ID for new model work can make its step IDs collide with earlier charges. If runId is omitted, telemetry generates UUIDs when creating events.
The adapter also works with generateText and streamText through the same AI SDK 6 experimental_telemetry.integrations option. Wire your checks and final assert() around those calls too.
Direct provider calls without Gateway cost metadata are not supported by this automatic inference adapter. Use check and track with your own measured quantities or callback pricing when your application calculates the cost itself.