Skip to content

.NET SDK — report usage and meter consumption

Oclavex distinguishes two kinds of usage. Reporting is telling the platform what happened (a feature was used, a session started) for analytics and utilization. Metering is drawing down a quota or credit balance, where the answer “is there room?” must be authoritative. The SDK exposes both off the session. Some reporting happens on its own: feature adoption is counted from the license checks your application already makes.

Every license check your application already makes is a record that someone reached for that feature, so adoption needs no reporting calls of its own:

if (!license.FeatureValidation.IsValidFeature("reports.export"))
return Forbid(); // counted as a refused check
// ... the feature runs ... counted as a granted check

Refused checks are counted separately from granted ones, and that separation is what makes them worth having: a feature customers are repeatedly refused is one they would pay for.

Gating is counted, reading is not. IsValidFeature and ValidateUsageQuota decide whether a user may do something, so they count. GetLicenseFeature and LicensedFeatures only read the license, so a pricing table or feature matrix that renders every capability does not inflate the usage of every feature on it. If your code gates by fetching a feature and inspecting it, switch it to IsValidFeature(code) so the check is counted here.

Set Usage.AutoReportFeatureChecks to false to turn this off. Nothing else you report is affected.

session.UsageReporter records activity. Reports are buffered and flushed on the UsageReportingIntervalSeconds interval you configure, so calls are cheap and don’t block your request:

await session.UsageReporter.ReportStartFeatureUseAsync(feature);
// ... feature runs ...
await session.UsageReporter.ReportEndFeatureUsageAsync(feature);

Use that pair when you want to know how long a feature was open and how activities nested. Which features are used at all is already counted from your license checks, so you do not need it for adoption.

Other reports on the same reporter cover session lifecycle (ReportSessionStartedAsync / ReportSessionEndedAsync), a raw asset amount (ReportAssetUsageAsync), application events (ReportAppEventAsync), and feedback (ReportProductFeedbackAsync, ReportFeatureRatingAsync). The background reporter flushes buffered usage on shutdown within ShutdownFlushTimeoutSeconds.

Transactions queue locally — on disk with FileTransactionStore, or the in-memory default — and upload in the background, so reporting keeps working through network drops. In a hosted app, register your own ITransactionStore (for example a FileTransactionStore) before AddMonetizeItLicenseConsumer to make the buffer durable across restarts. On the fluent-builder path, use UseFileTransactionStore(path) or UseTransactionProcessorBuilder(...), and call client.FlushAsync() to drain the queue on demand.

When a metric has to enforce a limit — API calls, credits, seats-worth of a resource — use session.AssetConsumption. TryConsumeAsync draws down the amount and returns the resulting status; the server settles concurrent draw-down so two callers can’t both spend the last unit:

var status = await session.AssetConsumption.TryConsumeAsync(
license.LicenseValidation.CurrentSession, "api-calls.total", amount: 1);
if (status is null ||
status.QuotaUsagePolicy is TierUsagePolicy.Deny or TierUsagePolicy.SuspendUsage)
{
return Results.StatusCode(429); // limit reached — stop the action
}

The returned status carries the enforcement decision, not just a number. QuotaUsagePolicy ranges from None/NotifyOnly/AllowWithOverage/AllowWithGrace (proceed) through RateLimit (throttle) to Deny/SuspendUsage (hard stop) — so a plan that permits billed overage and one that blocks outright are both expressible without your code special-casing them. CurrentValue, Limit, UsagePercentage, and PeriodResetAt describe where the meter stands and when it rolls over.

To show remaining allowance without consuming, use GetUsageStatusAsync, or GetUsageWithBalanceAsync when you want the full picture including any prepaid credit balance. Pass the session id from license.LicenseValidation.CurrentSession.

Before starting work that is expensive to throw away, ask first. TryPreAllocateAsync answers the question TryConsumeAsync would answer — allowed or refused, and what it costs — without changing anything: nothing is metered, nothing is charged, and nothing is held. Do the work, then commit it:

var check = await session.AssetConsumption.TryPreAllocateAsync(sessionId, "video.seconds", amount: 6);
if (check?.QuotaUsagePolicy is TierUsagePolicy.Deny or TierUsagePolicy.SuspendUsage)
{
// check.Reason says whether it is the plan limit or the credits: NoCredit,
// SpendingDisabled, BalanceExpired, BalanceSuspended — None means the quota itself.
return Results.StatusCode(429);
}
// ... render the six seconds ...
var status = await session.AssetConsumption.TryConsumeAsync(
sessionId, "video.seconds", amount: 6, idempotentKey: renderId);

Pass an idempotentKey on the commit so a retry after a dropped response returns the original decision instead of metering the work twice.

Because nothing is reserved between the two calls, a concurrent commit can still take the capacity the projection saw — the commit is the decision that binds, and it can still refuse. Check its answer, not only the projection’s.

When the quota’s overage tier is funded by a credit balance, a successful commit says what it cost: CreditsCharged and FundedUnits — the part of your amount that fell past the included quota. Six seconds committed with one funded unit is “five seconds included, one second from your credits”.

  • For adoption — which features are reached for, and which are refused — call nothing. Your license checks are already counted.
  • Use UsageReporter for activity you want to see later: how long a feature was open, session counts, application events. It is fire-and-then-flush; a dropped report costs a data point, not correctness.
  • Use AssetConsumption.TryConsumeAsync when the number governs behavior — when exceeding it must stop the action. The server answers before the action proceeds and settles concurrent draw-down.

One overlap is worth knowing. ReportAssetUsageAsync names a monitored asset, so when that asset carries quota tiers or a credit balance, the queued readings are priced and charged on arrival exactly as a commit would be — consumption costs the same whether the network was there or not. What an upload cannot do is be refused: the work has already happened. Reach for TryConsumeAsync when you need the answer before it runs.

GetFeatureRatingPromptAsync asks the platform which feature (if any) the current user should be prompted to rate — targeted, not random. Render your own prompt when it returns a target, then submit with ReportFeatureRatingAsync. It returns null when nothing is targeted.