For a streamed OpenAI Responses call, account cost becomes measurable only after the stream exposes its final usage. Wrap the official client on your server, attach an opaque account ID before the call, and record the provider-returned usage once the final response.completed event arrives.
Wait for the final usage evidence
Text deltas are useful for user experience, but they are not the accounting record. OpenAI's final response.completed event includes the completed Response and its usage object. That usage separates input_tokens, output_tokens, and cached input through input_tokens_details.cached_tokens.
If a stream is cancelled before final usage arrives, keep the call visible with unknown usage. Do not record zero tokens and do not estimate the missing total from text length.
Wrap the server client and add business identity
import OpenAI from "openai";
import { GrowOrDie } from "@grow-or-die/sdk";
const telemetry = new GrowOrDie({
ingestKey: process.env.GROW_OR_DIE_KEY,
});
const openai = telemetry.wrapOpenAI(new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
maxRetries: 0,
}));
const stream = await telemetry.context({
accountId: "account_123",
userId: "user_456",
feature: "support-agent",
promptVersion: "v4",
logicalRequestId: "request_01JABC",
attempt: 1,
}, () => openai.responses.create({
model: "gpt-4.1",
input: "Your application input",
service_tier: "default",
stream: true,
}));
for await (const event of stream) {
// Preserve the application's normal streaming behavior.
}
await telemetry.flush();
The wrapper observes the same stream your application consumes. It does not upload prompts or responses, authorization headers, provider keys, or request bodies. Pricing happens later on the Grow or Die server using the exact model, service tier, date, and reported token categories.
Keep cached input separate
Observed call cost = uncached input cost + cached input cost + output cost + supported tool fees.
Cached tokens remain a separate meter because their price can differ from uncached input. If the model, service tier, or a non-zero billable category has no trusted price, the call remains unpriced rather than producing a partial total.
Link the account without email joins
Use the same stable internal accountId for model calls and for the server-side identity link to Stripe or Lemon Squeezy. An email address is mutable and can belong to several workspaces; it is not a safe accounting key. Only the canonical account join can support customer-level contribution profit.
What this evidence can and cannot prove
A final provider usage object proves the tokens reported for that exposed response. It does not reveal retries hidden inside the provider client, prove a provider invoice is complete, or connect the call to revenue unless the same opaque account ID is also linked to a payment customer.