Skip to content

feat(usage): Client.usage for metered usage and usage gates - #283

Merged
purpshell merged 8 commits into
devfrom
t3code/usage-metering-gates
Sep 23, 2026
Merged

purpshell merged 8 commits into
devfrom
t3code/usage-metering-gates

Conversation

@purpshell

@purpshell purpshell commented Sep 20, 2026 •

Copy link
Copy Markdown
Member

Adds typed usage and gate reads to the TypeScript SDK for the accepted call-metering contract in monorepo #228, merged on dev at fdaff9a86220e3ef1f8ad75cc838dbfd03ede4eb.

Client.usage.summary, listRecords, iterateRecords, and listGates cover GET /platform/usage, /platform/usage/records, and /platform/gates. Project clients stay bound to their own project. listGates still receives the API’s refusal for project credentials because gate state is team-wide. The SDK also exposes the usage.recorded webhook payload and preserves unpriced usage semantics; this change does not charge usage or enable hard limits.

The Messaging and Platform contract snapshots are byte-identical to merged monorepo dev at fdaff9a8, with no pending overlay. This branch normally merges SDK dev ad5e5620003514deec3a456a99bb827b7406bc32 (including analytics #281). Reconciled coverage is 459 operations: 348 covered, 111 excluded, zero missing or changed. The pre-existing local package.json packageManager edit was preserved and is not part of this PR.

Part Assessment
API Accepted upstream. Monorepo #228 owns the handlers, authorization, specs, usage records, gate policy, and webhook contract; this PR does not alter API runtime.
SDKs Updated. This TypeScript SDK adds methods, closed types, validation, pagination, and webhook types against the exact accepted source. No unsupported language parity is claimed.
CLI Pending exact SDK publication. Existing CLI #28 has local typed usage commands and must pin the SDK prerelease produced after this PR merges.
Docs Updated. SDK README and changelog cover the methods and upgrade path; the authoritative and public API references plus internal release record live in the merged monorepo change. The customer Calls release remains unannounced.
Feature releases Unchanged. Calls remains unavailable to customers; usage is measured, unpriced, and the outgoing monthly gate remains record-only. No release flag, audience, entitlement, price, or deployment changed.
Admin Unaffected. Admin’s aggregate usage/gate authority is owned by merged API #228; this SDK adds no staff capability.

The full repository verification sequence passed under both Node 20 and Node 22: clean install, format, lint, typecheck, workspace builds, tests, coverage, name checks, package dry run, and production audit. One existing media abort cleanup test failed on the first Node 20 run, then passed in isolation and in a complete clean-install rerun. No live API, npm publication, or deployed environment was exercised by these local checks; fresh current-head CI and reviews are required before merge.

Adds Client.usage.summary, listRecords, iterateRecords and listGates over
/platform/usage, /platform/usage/records and /platform/gates, the
usage.recorded webhook payload, and the bansafe.risk_changed and
bansafe.health_changed payloads the re-pinned contract publishes.

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

  • Ask an admin to enable usage-based reviews

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Next included review available in 17 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 80 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

The included review limit has been reached and this organization has disabled usage-based review continuation. Wait for reviews to reset or ask a billing admin to change After included review limits.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 179e886c-4918-41a7-b8a5-b3ef66f22ee2

📥 Commits

Reviewing files that changed from the base of the PR and between a546a83 and be1b75d.

📒 Files selected for processing (15)
  • README.md
  • contracts/README.md
  • contracts/coverage.json
  • contracts/openapi.messaging.json
  • contracts/openapi.platform.json
  • contracts/source.json
  • packages/typescript/src/client.ts
  • packages/typescript/src/index.ts
  • packages/typescript/src/platform/usage.ts
  • packages/typescript/src/webhooks/events.ts
  • packages/typescript/src/webhooks/index.ts
  • packages/typescript/test/coverage-reconciliation.test.ts
  • packages/typescript/test/coverage.test.ts
  • packages/typescript/test/usage.test.ts
  • packages/typescript/test/webhook-contract.test.ts

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude <noreply@anthropic.com>
@purpshell

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T13:18:26.579396Z 1eea217 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 96208fd436

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +884 to +886
readonly meter: string;
readonly quantity: number;
readonly unit: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the closed meter and unit types in webhook payloads

For consumers constructing or exhaustively handling usage.recorded, these string annotations accept values that the pinned webhook contract rejects: both fields are closed enums, already represented by the exported UsageMeter and UsageUnit types. Using those types here prevents invalid payloads such as meter: "unknown" from compiling and preserves exhaustive handling.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in be1b75d: UsageRecordedPayload now uses UsageMeter, UsageUnit, UsageKeySource, UsageSourceKind and UsagePricingState, and a type test asserts it equals UsageRecord.

/** One contributing factor behind a risk level. */
export interface BanSafeRiskFactor {
readonly key: string;
readonly group: string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the closed BanSafe factor-group enum

When consumers branch on risk-factor groups, typing group as an arbitrary string loses the contract's closed 16-value enum and allows unsupported group names to compile. Define the corresponding string-literal union here so bansafe.risk_changed remains as strongly typed as its other enum fields.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changed on this branch. sdks dev is getting a named BanSafeRiskFactorGroup union through sdks#282; the coordinator's dev merge into this branch resolves it, so this PR leaves the BanSafe block untouched to avoid a conflicting definition.

Comment on lines +239 to +240
* Gate state covers the whole team, so this needs an organization client;
* the API refuses project credentials with 403.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document project-token access to usage gates correctly

For project-token users, this states the opposite of the pinned /platform/gates contract, whose operation description explicitly allows project tokens to access their own project; the implementation also exposes listGates on project clients and injects their bound projectId. Claiming that the API returns 403 can cause users to avoid a supported operation, so describe it as project-scoped instead.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the wording. The pinned snapshot in contracts/ predates the change; the current API contract (#228, openapi.management.json) says "Requires a team API key with sessions:read. Project tokens are not accepted." and the handler rejects project credentials with 403 (if (ctx.projectId) throw errForbidden("usage gates require a team credential") in routes/console/usage-handlers.ts). The snapshot refresh comes with the contract pin update, not this PR.

@purpshell

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 96208fd436

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@purpshell

Copy link
Copy Markdown
Member Author

@codex review

…-month records

The usage.recorded payload now reuses UsageMeter, UsageUnit, UsageKeySource,
UsageSourceKind and UsagePricingState, matching UsageRecord. listRecords
documents that omitting period lists records from every month.

Co-Authored-By: Claude <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 96208fd436

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Resolve duplicate BanSafe exports and fixtures while retaining the closed usage webhook enums. Refresh both contract snapshots to pending API PR #228 commit 651c137, with an explicit final dev repin requirement and SDK #282 dependency.

Node 20 and 22 typechecks pass; all workspace builds, format, lint, coverage, package checks and production audit pass. Full suites have 1068 passing tests and the two known SDK #282 contract failures, which remain unchanged until that dependency merges.
…gates

# Conflicts:
#	README.md
#	contracts/README.md
#	contracts/coverage.json
#	contracts/openapi.platform.json
#	contracts/source.json
#	packages/typescript/src/client.ts
#	packages/typescript/test/coverage-reconciliation.test.ts
#	packages/typescript/test/coverage.test.ts
@purpshell

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d4fcf13acb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +235 to +237
yield* page.data.records;
cursor = page.data.nextCursor ?? undefined;
} while (cursor);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject repeated record cursors before yielding the page

When the API or a cache returns a stale page whose nextCursor was already requested, iterateRecords() requests that cursor forever and repeatedly yields the same usage records, which can duplicate downstream aggregation and generate unbounded traffic. Track requested cursors and reject a repeated cursor before yielding the replayed page, as the existing Calls export iterator does.

Useful? React with 👍 / 👎.

Comment thread README.md Outdated
value: process.env.POLYMORFA_MESSAGING_API_KEY!,
},
apiVersion: "2026-03-20",
apiVersion: "1.0.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a supported contract date in the example

When users copy this quick-start example, the transport sends 1.0.0 verbatim in Polymorfa-Version, but this option expects a supported date-form contract version such as 2026-03-20—the format still used in packages/typescript/README.md. The example therefore causes otherwise valid Messaging requests to select an unsupported API version.

Useful? React with 👍 / 👎.

Comment thread README.md Outdated
protocol. Its `pmfa_ls_` credential cannot be used by `Client`,
`MessagingClient`, or their raw request helpers. Browser client tokens cannot
use the server event stream.
The SDK has no listener, event stream, `AsyncIterable`, or forwarding API.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the documented server event stream

This statement is false for the reviewed SDK: Client.events.stream() returns an AsyncIterable, Client.events.liveSource() is also present, and the pinned contract still includes the stream and acknowledgement routes. Users relying on the root guide are incorrectly told that a supported API does not exist, contradicting the package-level streaming guide.

Useful? React with 👍 / 👎.

@purpshell

Copy link
Copy Markdown
Member Author

Current head 1eea217: fixed the valid repeated-cursor, Messaging version example, and server event-stream documentation findings. The gate project-token suggestion does not match merged API #228: its authoritative management OpenAPI requires a team key, and usage-handlers.ts rejects ctx.projectId with 403. Focused usage tests, typecheck, lint, format, and the full 1,151-test rerun pass (one unrelated media abort cleanup race failed on the first run). The pre-existing packageManager worktree edit remains uncommitted. @codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 1eea217640

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@purpshell

Copy link
Copy Markdown
Member Author

Merge gate for exact head 1eea217: current-head Node 20 and 22 CI passed, and GitHub Codex completed a clean current-head review. The valid preceding review findings were fixed and the project-token gate suggestion was checked against merged API #228 and found incorrect. Local focused usage tests, typecheck, lint, format, and the full 1,151-test rerun passed; the pre-existing unrelated packageManager edit was excluded from the PR. CodeRabbit did not deliver a clean review. The owner explicitly authorized merging #283 with CI + Codex and recording that exception. Calls remains unreleased; this SDK merge does not deploy or enable it.

@purpshell
purpshell merged commit eaa7f67 into dev Sep 23, 2026
3 checks passed
@purpshell
purpshell deleted the t3code/usage-metering-gates branch October 26, 2026 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant