@company-semantics/contracts 94.0.0 → 95.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "94.0.0",
3
+ "version": "95.0.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '826e3d4d23ef' as const;
3
- export const SPEC_HASH_FULL = '826e3d4d23ef986d25b22b32497686755ce2b9bebc0ad90efd7768b4e1e076de' as const;
2
+ export const SPEC_HASH = 'd4db95eeefb4' as const;
3
+ export const SPEC_HASH_FULL = 'd4db95eeefb4bf7a97541fcefdcb90c1804e1e39508fe0b98e6389ce001a6f16' as const;
@@ -10129,17 +10129,38 @@ export interface components {
10129
10129
  hostedSurchargeBps: number;
10130
10130
  byoFeeBps: number;
10131
10131
  priceBookVersion: string;
10132
+ /** Format: date-time */
10133
+ priceBookEffectiveAt: string;
10132
10134
  models: {
10133
10135
  /** @enum {string} */
10134
10136
  provider: "openai" | "anthropic";
10135
10137
  modelId: string;
10136
- inputPerMMicros: string;
10137
- outputPerMMicros: string;
10138
- cacheReadPerMMicros: string;
10139
- cacheWritePerMMicros: string;
10140
- reasoningPerMMicros: string;
10141
- hostedInputPerMMicros: string;
10142
- hostedOutputPerMMicros: string;
10138
+ aliases: string[];
10139
+ sourceUrl: string | null;
10140
+ retrievedOn: string | null;
10141
+ promptTierMeasure: ("input_incl_cache" | "input_excl_cache") | null;
10142
+ promptTokensCeiling: number | null;
10143
+ tiers: {
10144
+ /** @enum {string} */
10145
+ processingMode: "standard" | "batch" | "flex" | "fast" | "ultrafast";
10146
+ promptTokensAbove: number;
10147
+ reference: {
10148
+ inputPerMMicros: string;
10149
+ outputPerMMicros: string | null;
10150
+ cacheReadPerMMicros: string | null;
10151
+ cacheWritePerMMicros: string | null;
10152
+ cacheWrite1hPerMMicros: string | null;
10153
+ reasoningPerMMicros: string | null;
10154
+ };
10155
+ hosted: {
10156
+ inputPerMMicros: string;
10157
+ outputPerMMicros: string | null;
10158
+ cacheReadPerMMicros: string | null;
10159
+ cacheWritePerMMicros: string | null;
10160
+ cacheWrite1hPerMMicros: string | null;
10161
+ reasoningPerMMicros: string | null;
10162
+ };
10163
+ }[];
10143
10164
  }[];
10144
10165
  };
10145
10166
  SourceSyncDiscoveryRunResponse: {
@@ -18,7 +18,8 @@ What it publishes:
18
18
  - **Closed enums**, each an `as const` tuple with a union type and a `z.enum`
19
19
  built from the same tuple: credential source, funding mode, platform status,
20
20
  AI pause reason, ledger entry type, reservation state, settlement basis,
21
- top-up kind and status, token dimension, principal kind, and provider.
21
+ top-up kind and status, token dimension, principal kind, provider, processing
22
+ mode and prompt tier measure.
22
23
  - **Derived tables.** `SEAT_BILLABILITY`, the mapping from principal kind to
23
24
  seat billability, and `TokenDimensionsSchema`, one integer count per token
24
25
  dimension.
@@ -99,6 +100,41 @@ owner is always its owner contradicts ADR-BE-296, and nothing imported them.
99
100
  who paid and was never credited, and `succeeded` and `credited` stay distinct
100
101
  in `TOP_UP_STATUSES` so that gap is visible to an operator.
101
102
 
103
+ ## Price list card (ADR slug price-list-tiers)
104
+
105
+ `PriceListSchema` publishes one card per model (`PriceListModelSchema`), not one
106
+ flat rate row. The card mirrors the provider's own pricing structure, so a
107
+ customer can check every charge against the provider's published page.
108
+
109
+ - **A card is `tiers[]`**: one `PriceListTierSchema` entry per
110
+ (`processingMode`, `promptTokensAbove`). Each tier carries the provider's
111
+ `reference` rates and the `hosted` rates derived from them, both as
112
+ `PriceListRatesSchema` (micro-USD per million tokens). Every priced model has a
113
+ `standard` tier at `promptTokensAbove: 0`; a request whose prompt is above a
114
+ tier's threshold pays that tier's rates on every dimension.
115
+ - **A processing mode is an explicit provider rate set, never a multiplier we
116
+ derive.** `PROCESSING_MODES` names the sets (standard, batch, flex, fast,
117
+ ultrafast); a mode's rates are the provider's published numbers, because
118
+ providers do not price modes by a clean factor (OpenAI gpt-4o Fast is 1.7x
119
+ Standard).
120
+ - **`null` means the provider publishes no separate rate** for that dimension —
121
+ never `"0"`. An input-side null (cache read, cache write) bills at the tier's
122
+ input rate, which is what the provider charges. `cacheWrite1hPerMMicros` is
123
+ published for transparency; the backend refuses 1-hour-TTL requests until a
124
+ usage axis exists for it (`TokenDimensionsSchema` is unchanged).
125
+ - **A model with more than one tier names its `promptTierMeasure`**
126
+ (`PROMPT_TIER_MEASURES`): what the provider counts to choose the tier. An
127
+ ambiguous provider rule is never guessed; the long tier stays unpublished and
128
+ `promptTokensCeiling` refuses prompts above the published range.
129
+ - **An id is listed once** per price list: as a card's `modelId` or as one
130
+ card's `aliases` entry, never both.
131
+ - **Corrections publish a new price-book version; history is never
132
+ recomputed.** `priceBookEffectiveAt` says when a version took effect,
133
+ `PriceBookVersionListSchema` lists every version that has ever priced a
134
+ charge, and `PriceListQuerySchema` selects one by label (omit for the active
135
+ version). The query schema is the one request-side shape here, published
136
+ because the version label is part of the price list's public promise.
137
+
102
138
  <!-- BEGIN GENERATED: readme-public-api — derived from code by `pnpm readme-api`. Do not edit. -->
103
139
 
104
140
  ## Public API
@@ -169,14 +205,30 @@ owner is always its owner contradicts ADR-BE-296, and nothing imported them.
169
205
  - `OrgBillingOverview` _(type)_ — Everything the org's billing page needs, in one response.
170
206
  - `OrgBillingOverviewSchema` — Everything the org's billing page needs, in ONE response.
171
207
  - `PRINCIPAL_KINDS` — WHAT KIND of thing an org membership represents.
208
+ - `PROCESSING_MODES` — How a provider processes a request, which selects the rate set it is billed at.
209
+ - `PROMPT_TIER_MEASURES` — What a provider counts to decide which prompt-length tier a request pays.
172
210
  - `PortalSessionResponse` _(type)_ — A Stripe customer-portal session to send the customer to.
173
211
  - `PortalSessionResponseSchema` — A Stripe customer-portal session to send the customer to.
212
+ - `PriceBookVersionList` _(type)_ — The price book's version history, newest first.
213
+ - `PriceBookVersionListSchema` — The price book's version history, newest first.
214
+ - `PriceBookVersionSummary` _(type)_ — One published price-book version.
215
+ - `PriceBookVersionSummarySchema` — One published price-book version, newest first in the list.
174
216
  - `PriceList` _(type)_ — The published price book, versioned.
175
- - `PriceListModel` _(type)_ — One priced model.
176
- - `PriceListModelSchema` — One priced model.
217
+ - `PriceListModel` _(type)_ — One priced model's card: provenance, aliases and tiers per mode.
218
+ - `PriceListModelSchema` — One priced model's card: its provenance, the ids it serves, and its tiers per processing mode.
219
+ - `PriceListQuery` _(type)_ — The price-list query: an optional version label, else the active one.
220
+ - `PriceListQuerySchema` — `GET /api/billing/price-list?version=<label>` — omit for the active version.
221
+ - `PriceListRates` _(type)_ — One rate set, micro-USD per million tokens; null = no separate rate.
222
+ - `PriceListRatesSchema` — One rate set, micro-USD PER MILLION tokens.
177
223
  - `PriceListSchema` — The published price book, versioned.
224
+ - `PriceListTier` _(type)_ — One tier of one processing mode: reference and hosted rates.
225
+ - `PriceListTierSchema` — One tier of one processing mode.
178
226
  - `PrincipalKind` _(type)_ — What kind of thing an org membership represents.
179
227
  - `PrincipalKindSchema`
228
+ - `ProcessingMode` _(type)_
229
+ - `ProcessingModeSchema`
230
+ - `PromptTierMeasure` _(type)_
231
+ - `PromptTierMeasureSchema`
180
232
  - `ProviderKeyList` _(type)_ — Every provider the org could configure a key for.
181
233
  - `ProviderKeyListSchema` — Every provider the org could configure a key for, configured or not.
182
234
  - `ProviderKeyStatus` _(type)_ — What an org may see about one of its own provider keys.
@@ -0,0 +1,129 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ PriceListModelSchema,
4
+ PriceListSchema,
5
+ PriceBookVersionListSchema,
6
+ } from "../schemas";
7
+ import { PROCESSING_MODES, PROMPT_TIER_MEASURES } from "../vocabulary";
8
+
9
+ const rates = (input: string) => ({
10
+ inputPerMMicros: input,
11
+ outputPerMMicros: "500000",
12
+ cacheReadPerMMicros: "10000",
13
+ cacheWritePerMMicros: "125000",
14
+ cacheWrite1hPerMMicros: "200000",
15
+ reasoningPerMMicros: "500000",
16
+ });
17
+ const haiku55 = {
18
+ provider: "anthropic",
19
+ modelId: "claude-haiku-5-5",
20
+ aliases: [],
21
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing",
22
+ retrievedOn: "2026-10-09",
23
+ promptTierMeasure: "input_incl_cache",
24
+ promptTokensCeiling: null,
25
+ tiers: [
26
+ {
27
+ processingMode: "standard",
28
+ promptTokensAbove: 0,
29
+ reference: rates("100000"),
30
+ hosted: rates("120000"),
31
+ },
32
+ {
33
+ processingMode: "standard",
34
+ promptTokensAbove: 100000,
35
+ reference: rates("500000"),
36
+ hosted: rates("600000"),
37
+ },
38
+ ],
39
+ };
40
+
41
+ describe("price list tiers", () => {
42
+ it("accepts a two-tier model that names its measure", () => {
43
+ expect(PriceListModelSchema.safeParse(haiku55).success).toBe(true);
44
+ });
45
+ it("refuses two tiers without a promptTierMeasure", () => {
46
+ expect(
47
+ PriceListModelSchema.safeParse({ ...haiku55, promptTierMeasure: null })
48
+ .success,
49
+ ).toBe(false);
50
+ });
51
+ it("refuses a model with no standard base tier", () => {
52
+ expect(
53
+ PriceListModelSchema.safeParse({ ...haiku55, tiers: [haiku55.tiers[1]] })
54
+ .success,
55
+ ).toBe(false);
56
+ });
57
+ it("accepts a null cache read as not separately priced", () => {
58
+ const t = {
59
+ ...haiku55.tiers[0],
60
+ reference: { ...rates("500000"), cacheReadPerMMicros: null },
61
+ };
62
+ expect(
63
+ PriceListModelSchema.safeParse({
64
+ ...haiku55,
65
+ promptTierMeasure: null,
66
+ tiers: [t],
67
+ }).success,
68
+ ).toBe(true);
69
+ });
70
+ it("accepts a pre-catalog card with no source", () => {
71
+ expect(
72
+ PriceListModelSchema.safeParse({
73
+ ...haiku55,
74
+ sourceUrl: null,
75
+ retrievedOn: null,
76
+ }).success,
77
+ ).toBe(true);
78
+ });
79
+ it("refuses an alias that is also listed as its own model", () => {
80
+ const dated = { ...haiku55, modelId: "claude-haiku-5-5-20261001" };
81
+ expect(
82
+ PriceListSchema.safeParse({
83
+ seatPriceMicros: null,
84
+ hostedSurchargeBps: 2000,
85
+ byoFeeBps: 500,
86
+ priceBookVersion: "pb-2026-10-v3",
87
+ priceBookEffectiveAt: "2026-10-10T00:00:00Z",
88
+ models: [{ ...haiku55, aliases: ["claude-haiku-5-5-20261001"] }, dated],
89
+ }).success,
90
+ ).toBe(false);
91
+ });
92
+ it("names the closed processing modes and tier measures", () => {
93
+ expect(PROCESSING_MODES).toEqual([
94
+ "standard",
95
+ "batch",
96
+ "flex",
97
+ "fast",
98
+ "ultrafast",
99
+ ]);
100
+ expect(PROMPT_TIER_MEASURES).toEqual([
101
+ "input_incl_cache",
102
+ "input_excl_cache",
103
+ ]);
104
+ });
105
+ it("requires the effective-at on the list and the version history shape", () => {
106
+ expect(
107
+ PriceListSchema.safeParse({
108
+ seatPriceMicros: null,
109
+ hostedSurchargeBps: 2000,
110
+ byoFeeBps: 500,
111
+ priceBookVersion: "pb-2026-10-v3",
112
+ models: [haiku55],
113
+ }).success,
114
+ ).toBe(false);
115
+ expect(
116
+ PriceBookVersionListSchema.safeParse({
117
+ versions: [
118
+ {
119
+ versionLabel: "pb-2026-10-v3",
120
+ effectiveAt: "2026-10-10T00:00:00Z",
121
+ source: "catalog",
122
+ notes: null,
123
+ active: true,
124
+ },
125
+ ],
126
+ }).success,
127
+ ).toBe(true);
128
+ });
129
+ });
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ PROCESSING_MODES,
4
+ ProcessingModeSchema,
5
+ PROMPT_TIER_MEASURES,
6
+ PromptTierMeasureSchema,
7
+ } from "../index";
8
+
9
+ describe("processing modes and prompt tier measures", () => {
10
+ it("exports the processing modes through the billing index", () => {
11
+ expect(PROCESSING_MODES).toContain("fast");
12
+ expect(PROMPT_TIER_MEASURES).toContain("input_excl_cache");
13
+ // "priority" was renamed to "fast"; the closed vocabulary refuses it.
14
+ expect(ProcessingModeSchema.safeParse("priority").success).toBe(false);
15
+ expect(PromptTierMeasureSchema.safeParse("input_incl_cache").success).toBe(
16
+ true,
17
+ );
18
+ });
19
+ });
@@ -179,27 +179,50 @@ describe("the billing response schemas", () => {
179
179
  });
180
180
 
181
181
  it("rejects a price list whose per-million rate is a number", () => {
182
- expect(
183
- PriceListSchema.safeParse({
184
- seatPriceMicros: null,
185
- hostedSurchargeBps: 2000,
186
- byoFeeBps: 500,
187
- priceBookVersion: "2026-09-01",
188
- models: [
189
- {
190
- provider: "anthropic",
191
- modelId: "claude-opus-5",
192
- inputPerMMicros: 15000000,
193
- outputPerMMicros: "75000000",
194
- cacheReadPerMMicros: "1500000",
195
- cacheWritePerMMicros: "18750000",
196
- reasoningPerMMicros: "75000000",
197
- hostedInputPerMMicros: "18000000",
198
- hostedOutputPerMMicros: "90000000",
199
- },
200
- ],
201
- }).success,
202
- ).toBe(false);
182
+ const rates = {
183
+ inputPerMMicros: "15000000",
184
+ outputPerMMicros: "75000000",
185
+ cacheReadPerMMicros: "1500000",
186
+ cacheWritePerMMicros: "18750000",
187
+ cacheWrite1hPerMMicros: "30000000",
188
+ reasoningPerMMicros: "75000000",
189
+ };
190
+ const priceList = (inputPerMMicros: string | number) => ({
191
+ seatPriceMicros: null,
192
+ hostedSurchargeBps: 2000,
193
+ byoFeeBps: 500,
194
+ priceBookVersion: "2026-09-01",
195
+ priceBookEffectiveAt: "2026-09-01T00:00:00Z",
196
+ models: [
197
+ {
198
+ provider: "anthropic",
199
+ modelId: "claude-opus-5",
200
+ aliases: [],
201
+ sourceUrl:
202
+ "https://platform.claude.com/docs/en/about-claude/pricing",
203
+ retrievedOn: "2026-09-01",
204
+ promptTierMeasure: null,
205
+ promptTokensCeiling: null,
206
+ tiers: [
207
+ {
208
+ processingMode: "standard",
209
+ promptTokensAbove: 0,
210
+ reference: { ...rates, inputPerMMicros },
211
+ hosted: { ...rates, inputPerMMicros: "18000000" },
212
+ },
213
+ ],
214
+ },
215
+ ],
216
+ });
217
+
218
+ // The same list with the rate as a string parses, so the only defect is
219
+ // the JSON number.
220
+ expect(PriceListSchema.safeParse(priceList("15000000")).success).toBe(
221
+ true,
222
+ );
223
+ expect(PriceListSchema.safeParse(priceList(15000000)).success).toBe(
224
+ false,
225
+ );
203
226
  });
204
227
  });
205
228
 
@@ -30,6 +30,8 @@ export type {
30
30
  TokenDimension,
31
31
  PrincipalKind,
32
32
  BillingProvider,
33
+ ProcessingMode,
34
+ PromptTierMeasure,
33
35
  SeatBillability,
34
36
  } from "./vocabulary";
35
37
 
@@ -60,6 +62,10 @@ export {
60
62
  PrincipalKindSchema,
61
63
  BILLING_PROVIDERS,
62
64
  BillingProviderSchema,
65
+ PROCESSING_MODES,
66
+ ProcessingModeSchema,
67
+ PROMPT_TIER_MEASURES,
68
+ PromptTierMeasureSchema,
63
69
  SEAT_BILLABILITY,
64
70
  TokenDimensionsSchema,
65
71
  } from "./vocabulary";
@@ -102,8 +108,13 @@ export {
102
108
  ModelAllowlistSchema,
103
109
  OrgBillingOverviewSchema,
104
110
  PortalSessionResponseSchema,
111
+ PriceBookVersionListSchema,
112
+ PriceBookVersionSummarySchema,
105
113
  PriceListModelSchema,
114
+ PriceListQuerySchema,
115
+ PriceListRatesSchema,
106
116
  PriceListSchema,
117
+ PriceListTierSchema,
107
118
  ProviderKeyListSchema,
108
119
  ProviderKeyStatusSchema,
109
120
  SpendingControlsSchema,
@@ -144,8 +155,13 @@ export type {
144
155
  ModelAllowlist,
145
156
  OrgBillingOverview,
146
157
  PortalSessionResponse,
158
+ PriceBookVersionList,
159
+ PriceBookVersionSummary,
147
160
  PriceList,
148
161
  PriceListModel,
162
+ PriceListQuery,
163
+ PriceListRates,
164
+ PriceListTier,
149
165
  ProviderKeyList,
150
166
  ProviderKeyStatus,
151
167
  SpendingControls,
@@ -19,7 +19,11 @@
19
19
  * - EVERY percentage is {@link BpsSchema} — an integer number of basis points.
20
20
  * `effectiveMarkupBps: 20.5` is not a fine-grained markup, it is a bug.
21
21
  * - EVERY timestamp is {@link IsoDateTime}, the package's single definition
22
- * site for a `format: date-time` field (ADR-CONTRACTS-156).
22
+ * site for a `format: date-time` field (ADR-CONTRACTS-156); the price book's
23
+ * effective-at timestamps use its offset-tolerant sibling.
24
+ * - A price-list rate that is `null` means the provider publishes no separate
25
+ * rate for it — never `"0"`. A processing mode is an explicit rate set, never
26
+ * a multiplier (ADR slug price-list-tiers).
23
27
  * - EVERY token count is a non-negative integer, reported per dimension via
24
28
  * {@link TokenDimensionsSchema} and never collapsed into one total
25
29
  * (INV-BILL-RAW-TOKENS-ALWAYS-VISIBLE).
@@ -35,7 +39,7 @@
35
39
  */
36
40
  import { z } from "zod";
37
41
 
38
- import { IsoDateTime } from "../api/primitives";
42
+ import { IsoDateTime, IsoDateTimeWithOffset } from "../api/primitives";
39
43
  import {
40
44
  AI_PAUSE_REASONS,
41
45
  BILLING_PLATFORM_STATUSES,
@@ -45,6 +49,9 @@ import {
45
49
  FUNDING_MODES,
46
50
  LEDGER_ENTRY_TYPES,
47
51
  MoneyMicrosSchema,
52
+ PROCESSING_MODES,
53
+ ProcessingModeSchema,
54
+ PromptTierMeasureSchema,
48
55
  TOP_UP_KINDS,
49
56
  TOP_UP_STATUSES,
50
57
  TokenDimensionsSchema,
@@ -412,37 +419,151 @@ export const ProviderKeyListSchema = z.object({
412
419
  /* -------------------------------------------------------------------------- */
413
420
 
414
421
  /**
415
- * One priced model. Every rate is micro-USD PER MILLION tokens, which is what
416
- * the `PerM` suffix means and the only unit any of them is ever in — the
417
- * provider publishes per-million prices, and converting on the way in would
418
- * round before anything has been metered.
422
+ * One rate set, micro-USD PER MILLION tokens. `null` means the provider publishes
423
+ * no separate rate for that dimension: an input-side dimension (cache read,
424
+ * cache write) is then billed at the tier's input rate, which is what the
425
+ * provider charges; an output-side null on a model is refused at the gate when
426
+ * the call could produce output. `cacheWritePerMMicros` is the provider's default
427
+ * (Anthropic: 5-minute) write; `cacheWrite1hPerMMicros` the Anthropic 1-hour write.
419
428
  *
420
- * The `hosted*` pair is the rate charged when this platform's credential paid.
421
- * There is no BYO counterpart: a BYO call is billed by the provider directly
422
- * and this platform charges `byoFeeBps` on it, not a per-token rate.
429
+ * Null is never encoded as `"0"`: zero would be a price (free), not the absence
430
+ * of one.
423
431
  */
424
- export const PriceListModelSchema = z.object({
425
- provider: z.enum(BILLING_PROVIDERS),
426
- modelId: z.string(),
432
+ export const PriceListRatesSchema = z.object({
427
433
  inputPerMMicros: MoneyMicrosSchema,
428
- outputPerMMicros: MoneyMicrosSchema,
429
- cacheReadPerMMicros: MoneyMicrosSchema,
430
- cacheWritePerMMicros: MoneyMicrosSchema,
431
- reasoningPerMMicros: MoneyMicrosSchema,
432
- hostedInputPerMMicros: MoneyMicrosSchema,
433
- hostedOutputPerMMicros: MoneyMicrosSchema,
434
+ outputPerMMicros: MoneyMicrosSchema.nullable(),
435
+ cacheReadPerMMicros: MoneyMicrosSchema.nullable(),
436
+ cacheWritePerMMicros: MoneyMicrosSchema.nullable(),
437
+ cacheWrite1hPerMMicros: MoneyMicrosSchema.nullable(),
438
+ reasoningPerMMicros: MoneyMicrosSchema.nullable(),
434
439
  });
435
440
 
441
+ /**
442
+ * One tier of one processing mode. `promptTokensAbove` is 0 for the base tier;
443
+ * a request whose prompt (per the model's `promptTierMeasure`) is ABOVE it pays
444
+ * this tier's rates on EVERY dimension. `reference` is the provider's list
445
+ * rate; `hosted` is what a hosted call is charged per million (reference plus
446
+ * the surcharge, same rounding as settlement, null preserved).
447
+ *
448
+ * A processing mode is its own explicit rate set, never a multiplier on the
449
+ * standard one: the providers do not publish clean multipliers.
450
+ */
451
+ export const PriceListTierSchema = z.object({
452
+ processingMode: ProcessingModeSchema,
453
+ promptTokensAbove: z.number().int().min(0),
454
+ reference: PriceListRatesSchema,
455
+ hosted: PriceListRatesSchema,
456
+ });
457
+
458
+ /**
459
+ * One priced model's card: its provenance, the ids it serves, and its tiers
460
+ * per processing mode. Every model has a `standard` base tier; a model with
461
+ * more than one tier in any mode names the {@link PromptTierMeasureSchema} that
462
+ * decides which tier a request falls in.
463
+ *
464
+ * There is no BYO rate: a BYO call is billed by the provider directly and this
465
+ * platform charges `byoFeeBps` on it, not a per-token rate.
466
+ */
467
+ export const PriceListModelSchema = z
468
+ .object({
469
+ provider: z.enum(BILLING_PROVIDERS),
470
+ modelId: z.string().min(1),
471
+ /**
472
+ * Ids this version records as served by the same card; [] when the version
473
+ * records none. An id appears once in a price list, as a modelId or as an
474
+ * alias, never both.
475
+ */
476
+ aliases: z.array(z.string().min(1)),
477
+ /**
478
+ * The provider's official pricing page the card was authored from. Null for
479
+ * a price-book version authored before the catalog recorded provenance.
480
+ */
481
+ sourceUrl: z.string().url().nullable(),
482
+ /**
483
+ * ISO date (YYYY-MM-DD) the source was last read. Null for a price-book
484
+ * version authored before the catalog recorded provenance.
485
+ */
486
+ retrievedOn: z
487
+ .string()
488
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
489
+ .nullable(),
490
+ promptTierMeasure: PromptTierMeasureSchema.nullable(),
491
+ /** Prompts above this many tokens are refused (a tier we have not published). Null = no ceiling. */
492
+ promptTokensCeiling: z.number().int().positive().nullable(),
493
+ tiers: z.array(PriceListTierSchema).min(1),
494
+ })
495
+ .superRefine((m, ctx) => {
496
+ const standardBase = m.tiers.some(
497
+ (t) => t.processingMode === "standard" && t.promptTokensAbove === 0,
498
+ );
499
+ if (!standardBase) {
500
+ ctx.addIssue("every priced model has a standard base tier");
501
+ }
502
+ for (const mode of PROCESSING_MODES) {
503
+ const above = m.tiers
504
+ .filter((t) => t.processingMode === mode)
505
+ .map((t) => t.promptTokensAbove);
506
+ if (above.length > 1 && m.promptTierMeasure === null) {
507
+ ctx.addIssue(
508
+ "a model with more than one tier names its promptTierMeasure",
509
+ );
510
+ }
511
+ if (new Set(above).size !== above.length) {
512
+ ctx.addIssue(`duplicate tier threshold in mode ${mode}`);
513
+ }
514
+ }
515
+ });
516
+
436
517
  /**
437
518
  * The published price book, versioned. Read by the app to show what a model
438
519
  * costs before it is used, and by control to audit what it charged after.
520
+ * A correction is a new version; a published version is never recomputed.
439
521
  */
440
- export const PriceListSchema = z.object({
441
- seatPriceMicros: MoneyMicrosSchema.nullable(),
442
- hostedSurchargeBps: BpsSchema,
443
- byoFeeBps: BpsSchema,
444
- priceBookVersion: z.string(),
445
- models: z.array(PriceListModelSchema),
522
+ export const PriceListSchema = z
523
+ .object({
524
+ seatPriceMicros: MoneyMicrosSchema.nullable(),
525
+ hostedSurchargeBps: BpsSchema,
526
+ byoFeeBps: BpsSchema,
527
+ priceBookVersion: z.string(),
528
+ /** When this version became the one calls are priced against (ISO timestamp). */
529
+ priceBookEffectiveAt: IsoDateTimeWithOffset,
530
+ models: z.array(PriceListModelSchema),
531
+ })
532
+ .superRefine((list, ctx) => {
533
+ // An id is listed once: as a card's modelId or as one card's alias, never both.
534
+ const seen = new Set<string>();
535
+ for (const m of list.models) {
536
+ for (const id of [m.modelId, ...m.aliases]) {
537
+ if (seen.has(id)) {
538
+ ctx.addIssue(`model id ${id} is listed more than once`);
539
+ }
540
+ seen.add(id);
541
+ }
542
+ }
543
+ });
544
+
545
+ /**
546
+ * `GET /api/billing/price-list?version=<label>` — omit for the active version.
547
+ * A request shape published here by exception (ADR slug price-list-tiers): the
548
+ * app and the backend must agree on how a past version is addressed.
549
+ */
550
+ export const PriceListQuerySchema = z.object({
551
+ version: z.string().min(1).optional(),
552
+ });
553
+
554
+ /** One published price-book version, newest first in the list. */
555
+ export const PriceBookVersionSummarySchema = z.object({
556
+ versionLabel: z.string(),
557
+ effectiveAt: IsoDateTimeWithOffset,
558
+ source: z.string(),
559
+ notes: z.string().nullable(),
560
+ /** True for exactly the version calls are priced against now. */
561
+ active: z.boolean(),
562
+ });
563
+
564
+ /** The price book's version history, newest first. */
565
+ export const PriceBookVersionListSchema = z.object({
566
+ versions: z.array(PriceBookVersionSummarySchema),
446
567
  });
447
568
 
448
569
  /**
@@ -37,8 +37,13 @@ import type {
37
37
  ModelAllowlistSchema,
38
38
  OrgBillingOverviewSchema,
39
39
  PortalSessionResponseSchema,
40
+ PriceBookVersionListSchema,
41
+ PriceBookVersionSummarySchema,
40
42
  PriceListModelSchema,
43
+ PriceListQuerySchema,
44
+ PriceListRatesSchema,
41
45
  PriceListSchema,
46
+ PriceListTierSchema,
42
47
  ProviderKeyListSchema,
43
48
  ProviderKeyStatusSchema,
44
49
  SpendingControlsSchema,
@@ -151,11 +156,28 @@ export type ProviderKeyStatus = z.infer<typeof ProviderKeyStatusSchema>;
151
156
  /** Every provider the org could configure a key for. */
152
157
  export type ProviderKeyList = z.infer<typeof ProviderKeyListSchema>;
153
158
 
154
- /** One priced model. Every rate is micro-USD per million tokens. */
159
+ /** One rate set, micro-USD per million tokens; null = no separate rate. */
160
+ export type PriceListRates = z.infer<typeof PriceListRatesSchema>;
161
+
162
+ /** One tier of one processing mode: reference and hosted rates. */
163
+ export type PriceListTier = z.infer<typeof PriceListTierSchema>;
164
+
165
+ /** One priced model's card: provenance, aliases and tiers per mode. */
155
166
  export type PriceListModel = z.infer<typeof PriceListModelSchema>;
156
167
 
157
168
  /** The published price book, versioned. */
158
169
  export type PriceList = z.infer<typeof PriceListSchema>;
159
170
 
171
+ /** The price-list query: an optional version label, else the active one. */
172
+ export type PriceListQuery = z.infer<typeof PriceListQuerySchema>;
173
+
174
+ /** One published price-book version. */
175
+ export type PriceBookVersionSummary = z.infer<
176
+ typeof PriceBookVersionSummarySchema
177
+ >;
178
+
179
+ /** The price book's version history, newest first. */
180
+ export type PriceBookVersionList = z.infer<typeof PriceBookVersionListSchema>;
181
+
160
182
  /** A house-funded org's grant. */
161
183
  export type HouseFunding = z.infer<typeof HouseFundingSchema>;
@@ -332,6 +332,43 @@ export const BILLING_PROVIDERS = [
332
332
  export type BillingProvider = (typeof BILLING_PROVIDERS)[number];
333
333
  export const BillingProviderSchema = z.enum(BILLING_PROVIDERS);
334
334
 
335
+ /**
336
+ * How a provider processes a request, which selects the rate set it is billed
337
+ * at. Each mode is an EXPLICIT rate set published by the provider, never a
338
+ * multiplier we derive: OpenAI's gpt-4o "Fast" is 1.7x Standard, gpt-6-luna's is
339
+ * 2x, and Anthropic's Batch is 0.5x. "fast" is what OpenAI called "priority"
340
+ * until 2026-07-30 and what Anthropic calls fast mode.
341
+ */
342
+ export const PROCESSING_MODES = [
343
+ /** The provider's default processing. Every priced model has it. */
344
+ "standard",
345
+ /** Asynchronous batch processing. */
346
+ "batch",
347
+ /** OpenAI Flex: lower price, slower and best-effort capacity. */
348
+ "flex",
349
+ /** Premium latency (OpenAI Fast, formerly Priority; Anthropic fast mode). */
350
+ "fast",
351
+ /** OpenAI Ultrafast. */
352
+ "ultrafast",
353
+ ] as const;
354
+ export type ProcessingMode = (typeof PROCESSING_MODES)[number];
355
+ export const ProcessingModeSchema = z.enum(PROCESSING_MODES);
356
+
357
+ /**
358
+ * What a provider counts to decide which prompt-length tier a request pays.
359
+ * Anthropic (Claude Haiku 5.5, >100K): every input token, INCLUDING cache reads
360
+ * and cache writes. A model with more than one tier must name its measure; an
361
+ * ambiguous provider rule is never guessed (the long tier stays unpublished).
362
+ */
363
+ export const PROMPT_TIER_MEASURES = [
364
+ /** input + cache read + cache write. */
365
+ "input_incl_cache",
366
+ /** Fresh (uncached) input only. */
367
+ "input_excl_cache",
368
+ ] as const;
369
+ export type PromptTierMeasure = (typeof PROMPT_TIER_MEASURES)[number];
370
+ export const PromptTierMeasureSchema = z.enum(PROMPT_TIER_MEASURES);
371
+
335
372
  /* -------------------------------------------------------------------------- */
336
373
  /* Derived tables */
337
374
  /* -------------------------------------------------------------------------- */