Ingest modes
The SDK reports usage in one of Stigg’s two standard ways, chosen by the ingest mode:
For how the two differ, including how quickly limits are enforced, see Supported usage sources.
You can set the mode for the whole SDK, for one wrapped client, or for a single call. A more specific setting takes precedence:
- Usage reported to a feature in
syncmode must be a whole number. Stigg’s usage API accepts only whole numbers, so a fractional value is not reported and the failure is passed toonError. A credit currency accepts fractional amounts in either mode, and so does an event inasyncmode. - Credits you calculate in code are deducted directly. Usually the SDK reports raw metrics, such as token counts, and Stigg converts them into credits using your credit consumption settings. If you convert usage into credits yourself with a usage mapper, the SDK deducts the result straight from the credit balance, in either mode, so it is not converted twice. It recognizes these credits by name: the mapper’s output must use the ID of the credit currency the call is checked against. For example, a mapper that outputs
{ credits: 3 }for a call checked against{ currencyId: 'credits' }deducts 3 credits. The ID must match exactly: a mapper that outputs{ data_credit: 3 }for a call checked against{ currencyId: 'credits' }reports a feature nameddata_credit, which needs a meter like any other feature. - In
asyncmode, deducted credits can take a moment to show. The deduction is applied to the balance asynchronously, so a check made right after a call may not include it yet. Usesyncwhen each call must be deducted before the next check.
Configuring meters for async events
Inasync mode each call becomes one event:
- The event name is the vendor call that produced it, for example
anthropic.messages.create. Each provider’s event names are listed on its page, and all of them in Supported providers. Usage from a proxy you operate is named<vendor>.proxied, or theeventyou pass toingestFromHeaders(). - Every metric is a field on the event, under the names on the provider’s page.
stiggContext.eventName, for example eventName: { name: 'llm.call' }, to put several vendors behind one meter.
To bill a metric, create a metered feature with a raw-events meter that filters on the event name and sums the field. For example, output tokens from Anthropic:
dimensions hold:
- The metrics, under the names on the provider’s page. Here
costis in millionths of an OpenRouter credit. - Your own
dimensions, and the agent ID under your agent type’s attribution key (hereagentId) when you set one. Use them as meter filters or for governance attribution. - For gateways, the
modelthat served the call. - Every usage field the vendor returned, with a
raw.prefix. Nested fields are flattened into single values.
Usage mappers
By default the SDK reports exactly the usage the vendor returned, under the vendor’s own metric names, so the numbers in Stigg match the vendor’s own usage reports. This needs no configuration. Each provider’s metrics are listed on its page, and all of them in Supported providers. To report something else, setstiggContext.usageMapper to one of the mappers below, or to your own. Set it on wrap() as the client’s default, or pass it with each call to override the default for that call.
Weighted credits
Converts each call’s usage into credits in your code, using a rate table, and deducts them straight from the credit balance. Usually you do not need this. The standard way to charge credits for tokens is in Stigg: create a metered feature for each token metric and set its credit cost in the credit currency’s Credit consumption settings. See Credits. Use weighted credits instead when:- You want the rates in code, for example to charge different rates per model by passing a different mapper in each call’s
stiggContext, instead of creating a feature for each case in Stigg. - You want to skip meters. Credits calculated in code are deducted straight from the balance, with no metered feature or meter to configure. With
syncmode, the next check already includes them.
weightedMetrics() turns a rate table into a mapper that reports one number, typically to a credit currency or a single metered feature:
- The table is typed against the recipe’s own metric names, so a typo does not compile.
- A metric left out of the table is free.
- The total is not rounded. A credit currency accepts fractional amounts, so a call worth a fifth of a credit costs a fifth of a credit. When the target is a metered feature in
syncmode, which accepts only whole numbers, choose rates and aperthat divide evenly, or round in your own mapper. - A call that measured nothing reports nothing.
unitsPerTokens('credits', 1_000) reports one unit per 1,000 tokens of any kind. It does not round either.
A call attributed to an agent is the exception: Governance counts agent and team usage in whole units, so the SDK rounds the amount up to the next whole unit and logs that it did. See Agent governance.
wrap(), like any other stiggContext field. This keeps the wrapped client generic, for example to charge a different rate per model:
Normalized metrics across vendors
Vendors disagree on whether cached tokens are already counted inside the input total:
To price every vendor with one set of features, use
normalizedMetrics. It reports four non-overlapping counters under SDK-owned names: stigg_input_tokens, stigg_output_tokens, stigg_cache_read_tokens and stigg_cache_creation_tokens.
Your own mapper
When none of the built-in mappers fits, write your own. A mapper is a plain function: it receives the usage the recipe measured for a call, and returns what to report, as a{ featureOrCurrencyId: value } record. Each recipe exports a type for what it receives:
usageMapper:
sync mode must be a whole number.
The mapper also receives the rest of the measurement: usage (the four normalized token counters), dimensions, and estimated. estimated is true when a count is the SDK’s estimate rather than the vendor’s. This happens when a streamed Anthropic call is stopped before it finishes: Anthropic reports the output count only at the end of the stream, so the SDK estimates the output from the text received so far, at about four characters per token. To bill only counts the vendor reported, return {} when estimated is set:
Idempotency
Every report carries an idempotency key, so a retried report is not counted twice:- When the vendor returns a request ID that is stable across its own retries (for example an OpenRouter generation ID), that ID is used.
- Otherwise the SDK generates one per call.
- Set
stiggContext.idempotencyKeyon a call to choose your own, for example when you resume a stream and report the same turn again.
sync mode each metric is keyed separately as <key>:<metric>.
Errors and flushing
Reporting never breaks the call. If a report fails, the response is still returned and the failure is passed toonError:
onError logs a warning, so failures are visible before you wire it up.
Reports are sent in the background. In serverless functions, request-scoped workers and CLIs, drain them before the process exits:
flush() returns a summary of what was reported: the number of checks and ingests, failures, and the totals per metric.
Troubleshooting
The credit balance does not go down
A credit balance only goes down when the feature you report is mapped to that credit currency in Stigg. If it is not, every call succeeds and every report is accepted, but the balance never changes. Insync mode the SDK detects this from Stigg’s response and logs: